blob: 65a1de18d45f87270313de552ee486c6639a84d2 [file] [log] [blame]
Dennis Shen1dc9ad42023-05-12 00:21:55 +00001/*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Mårten Kongstadfbd71e22023-05-31 13:29:35 +020017use anyhow::{ensure, Result};
Dennis Shen1dc9ad42023-05-12 00:21:55 +000018use serde::Serialize;
19use tinytemplate::TinyTemplate;
20
21use crate::aconfig::{FlagState, Permission};
22use crate::cache::{Cache, Item};
Mårten Kongstadfbd71e22023-05-31 13:29:35 +020023use crate::codegen;
Dennis Shen1dc9ad42023-05-12 00:21:55 +000024use crate::commands::OutputFile;
25
26pub fn generate_cpp_code(cache: &Cache) -> Result<OutputFile> {
27 let class_elements: Vec<ClassElement> = cache.iter().map(create_class_element).collect();
28 let readwrite = class_elements.iter().any(|item| item.readwrite);
Mårten Kongstadfbd71e22023-05-31 13:29:35 +020029 let package = cache.package().to_string();
30 let header = package.replace('.', "_");
31 let cpp_namespace = package.replace('.', "::");
32 ensure!(codegen::is_valid_name_ident(&header));
33 let context =
34 Context { header: header.clone(), cpp_namespace, package, readwrite, class_elements };
Dennis Shen1dc9ad42023-05-12 00:21:55 +000035 let mut template = TinyTemplate::new();
36 template.add_template("cpp_code_gen", include_str!("../templates/cpp.template"))?;
37 let contents = template.render("cpp_code_gen", &context)?;
Mårten Kongstadfbd71e22023-05-31 13:29:35 +020038 let path = ["aconfig", &(header + ".h")].iter().collect();
Dennis Shen1dc9ad42023-05-12 00:21:55 +000039 Ok(OutputFile { contents: contents.into(), path })
40}
41
42#[derive(Serialize)]
43struct Context {
Mårten Kongstadfbd71e22023-05-31 13:29:35 +020044 pub header: String,
45 pub cpp_namespace: String,
Mårten Kongstad9fb58962023-05-31 13:02:13 +020046 pub package: String,
Dennis Shen1dc9ad42023-05-12 00:21:55 +000047 pub readwrite: bool,
48 pub class_elements: Vec<ClassElement>,
49}
50
51#[derive(Serialize)]
52struct ClassElement {
53 pub readwrite: bool,
54 pub default_value: String,
55 pub flag_name: String,
56}
57
58fn create_class_element(item: &Item) -> ClassElement {
59 ClassElement {
60 readwrite: item.permission == Permission::ReadWrite,
61 default_value: if item.state == FlagState::Enabled {
62 "true".to_string()
63 } else {
64 "false".to_string()
65 },
66 flag_name: item.name.clone(),
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use crate::aconfig::{FlagDeclaration, FlagState, FlagValue, Permission};
Mårten Kongstad2f954442023-05-17 16:51:16 +020074 use crate::cache::CacheBuilder;
Dennis Shen1dc9ad42023-05-12 00:21:55 +000075 use crate::commands::Source;
76
77 #[test]
78 fn test_cpp_codegen_build_time_flag_only() {
Mårten Kongstadfbd71e22023-05-31 13:29:35 +020079 let package = "com.example";
Mårten Kongstad9fb58962023-05-31 13:02:13 +020080 let mut builder = CacheBuilder::new(package.to_string()).unwrap();
Mårten Kongstad2f954442023-05-17 16:51:16 +020081 builder
Dennis Shen1dc9ad42023-05-12 00:21:55 +000082 .add_flag_declaration(
83 Source::File("aconfig_one.txt".to_string()),
84 FlagDeclaration {
85 name: "my_flag_one".to_string(),
86 description: "buildtime disable".to_string(),
87 },
88 )
Mårten Kongstad2f954442023-05-17 16:51:16 +020089 .unwrap()
Dennis Shen1dc9ad42023-05-12 00:21:55 +000090 .add_flag_value(
91 Source::Memory,
92 FlagValue {
Mårten Kongstad9fb58962023-05-31 13:02:13 +020093 package: package.to_string(),
Dennis Shen1dc9ad42023-05-12 00:21:55 +000094 name: "my_flag_one".to_string(),
95 state: FlagState::Disabled,
96 permission: Permission::ReadOnly,
97 },
98 )
Mårten Kongstad2f954442023-05-17 16:51:16 +020099 .unwrap()
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000100 .add_flag_declaration(
101 Source::File("aconfig_two.txt".to_string()),
102 FlagDeclaration {
103 name: "my_flag_two".to_string(),
104 description: "buildtime enable".to_string(),
105 },
106 )
Mårten Kongstad2f954442023-05-17 16:51:16 +0200107 .unwrap()
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000108 .add_flag_value(
109 Source::Memory,
110 FlagValue {
Mårten Kongstad9fb58962023-05-31 13:02:13 +0200111 package: package.to_string(),
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000112 name: "my_flag_two".to_string(),
113 state: FlagState::Enabled,
114 permission: Permission::ReadOnly,
115 },
116 )
117 .unwrap();
Mårten Kongstad2f954442023-05-17 16:51:16 +0200118 let cache = builder.build();
Mårten Kongstadfbd71e22023-05-31 13:29:35 +0200119 let expect_content = r#"#ifndef com_example_HEADER_H
120 #define com_example_HEADER_H
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000121
Mårten Kongstadfbd71e22023-05-31 13:29:35 +0200122 namespace com::example {
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000123
124 class my_flag_one {
125 public:
126 virtual const bool value() {
127 return false;
128 }
129 }
130
131 class my_flag_two {
132 public:
133 virtual const bool value() {
134 return true;
135 }
136 }
137
138 }
139 #endif
140 "#;
141 let file = generate_cpp_code(&cache).unwrap();
Mårten Kongstadfbd71e22023-05-31 13:29:35 +0200142 assert_eq!("aconfig/com_example.h", file.path.to_str().unwrap());
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000143 assert_eq!(
144 expect_content.replace(' ', ""),
145 String::from_utf8(file.contents).unwrap().replace(' ', "")
146 );
147 }
148
149 #[test]
150 fn test_cpp_codegen_runtime_flag() {
Mårten Kongstadfbd71e22023-05-31 13:29:35 +0200151 let package = "com.example";
Mårten Kongstad9fb58962023-05-31 13:02:13 +0200152 let mut builder = CacheBuilder::new(package.to_string()).unwrap();
Mårten Kongstad2f954442023-05-17 16:51:16 +0200153 builder
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000154 .add_flag_declaration(
155 Source::File("aconfig_one.txt".to_string()),
156 FlagDeclaration {
157 name: "my_flag_one".to_string(),
158 description: "buildtime disable".to_string(),
159 },
160 )
Mårten Kongstad2f954442023-05-17 16:51:16 +0200161 .unwrap()
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000162 .add_flag_declaration(
163 Source::File("aconfig_two.txt".to_string()),
164 FlagDeclaration {
165 name: "my_flag_two".to_string(),
166 description: "runtime enable".to_string(),
167 },
168 )
Mårten Kongstad2f954442023-05-17 16:51:16 +0200169 .unwrap()
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000170 .add_flag_value(
171 Source::Memory,
172 FlagValue {
Mårten Kongstad9fb58962023-05-31 13:02:13 +0200173 package: package.to_string(),
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000174 name: "my_flag_two".to_string(),
175 state: FlagState::Enabled,
176 permission: Permission::ReadWrite,
177 },
178 )
179 .unwrap();
Mårten Kongstad2f954442023-05-17 16:51:16 +0200180 let cache = builder.build();
Mårten Kongstadfbd71e22023-05-31 13:29:35 +0200181 let expect_content = r#"#ifndef com_example_HEADER_H
182 #define com_example_HEADER_H
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000183
184 #include <server_configurable_flags/get_flags.h>
185 using namespace server_configurable_flags;
186
Mårten Kongstadfbd71e22023-05-31 13:29:35 +0200187 namespace com::example {
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000188
189 class my_flag_one {
190 public:
191 virtual const bool value() {
192 return GetServerConfigurableFlag(
Mårten Kongstadfbd71e22023-05-31 13:29:35 +0200193 "com.example",
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000194 "my_flag_one",
195 "false") == "true";
196 }
197 }
198
199 class my_flag_two {
200 public:
201 virtual const bool value() {
202 return GetServerConfigurableFlag(
Mårten Kongstadfbd71e22023-05-31 13:29:35 +0200203 "com.example",
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000204 "my_flag_two",
205 "true") == "true";
206 }
207 }
208
209 }
210 #endif
211 "#;
212 let file = generate_cpp_code(&cache).unwrap();
Mårten Kongstadfbd71e22023-05-31 13:29:35 +0200213 assert_eq!("aconfig/com_example.h", file.path.to_str().unwrap());
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000214 assert_eq!(
215 expect_content.replace(' ', ""),
216 String::from_utf8(file.contents).unwrap().replace(' ', "")
217 );
218 }
219}