blob: 37b058df4f44a2096f2b0e44f3d85734c44dcfd0 [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> {
Mårten Kongstad066575b2023-06-07 16:29:25 +020027 let package = cache.package();
28 let class_elements: Vec<ClassElement> =
29 cache.iter().map(|item| create_class_element(package, item)).collect();
Dennis Shen1dc9ad42023-05-12 00:21:55 +000030 let readwrite = class_elements.iter().any(|item| item.readwrite);
Mårten Kongstadfbd71e22023-05-31 13:29:35 +020031 let header = package.replace('.', "_");
32 let cpp_namespace = package.replace('.', "::");
33 ensure!(codegen::is_valid_name_ident(&header));
Mårten Kongstad066575b2023-06-07 16:29:25 +020034 let context = Context {
35 header: header.clone(),
36 cpp_namespace,
37 package: package.to_string(),
38 readwrite,
39 class_elements,
40 };
Dennis Shen1dc9ad42023-05-12 00:21:55 +000041 let mut template = TinyTemplate::new();
42 template.add_template("cpp_code_gen", include_str!("../templates/cpp.template"))?;
43 let contents = template.render("cpp_code_gen", &context)?;
Mårten Kongstadfbd71e22023-05-31 13:29:35 +020044 let path = ["aconfig", &(header + ".h")].iter().collect();
Dennis Shen1dc9ad42023-05-12 00:21:55 +000045 Ok(OutputFile { contents: contents.into(), path })
46}
47
48#[derive(Serialize)]
49struct Context {
Mårten Kongstadfbd71e22023-05-31 13:29:35 +020050 pub header: String,
51 pub cpp_namespace: String,
Mårten Kongstad9fb58962023-05-31 13:02:13 +020052 pub package: String,
Dennis Shen1dc9ad42023-05-12 00:21:55 +000053 pub readwrite: bool,
54 pub class_elements: Vec<ClassElement>,
55}
56
57#[derive(Serialize)]
58struct ClassElement {
59 pub readwrite: bool,
60 pub default_value: String,
61 pub flag_name: String,
Mårten Kongstad066575b2023-06-07 16:29:25 +020062 pub device_config_namespace: String,
63 pub device_config_flag: String,
Dennis Shen1dc9ad42023-05-12 00:21:55 +000064}
65
Mårten Kongstad066575b2023-06-07 16:29:25 +020066fn create_class_element(package: &str, item: &Item) -> ClassElement {
Dennis Shen1dc9ad42023-05-12 00:21:55 +000067 ClassElement {
68 readwrite: item.permission == Permission::ReadWrite,
69 default_value: if item.state == FlagState::Enabled {
70 "true".to_string()
71 } else {
72 "false".to_string()
73 },
74 flag_name: item.name.clone(),
Mårten Kongstad066575b2023-06-07 16:29:25 +020075 device_config_namespace: item.namespace.to_string(),
76 device_config_flag: codegen::create_device_config_ident(package, &item.name)
77 .expect("values checked at cache creation time"),
Dennis Shen1dc9ad42023-05-12 00:21:55 +000078 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84 use crate::aconfig::{FlagDeclaration, FlagState, FlagValue, Permission};
Mårten Kongstad2f954442023-05-17 16:51:16 +020085 use crate::cache::CacheBuilder;
Dennis Shen1dc9ad42023-05-12 00:21:55 +000086 use crate::commands::Source;
87
88 #[test]
89 fn test_cpp_codegen_build_time_flag_only() {
Mårten Kongstadfbd71e22023-05-31 13:29:35 +020090 let package = "com.example";
Mårten Kongstad9fb58962023-05-31 13:02:13 +020091 let mut builder = CacheBuilder::new(package.to_string()).unwrap();
Mårten Kongstad2f954442023-05-17 16:51:16 +020092 builder
Dennis Shen1dc9ad42023-05-12 00:21:55 +000093 .add_flag_declaration(
94 Source::File("aconfig_one.txt".to_string()),
95 FlagDeclaration {
96 name: "my_flag_one".to_string(),
Mårten Kongstad066575b2023-06-07 16:29:25 +020097 namespace: "ns".to_string(),
Dennis Shen1dc9ad42023-05-12 00:21:55 +000098 description: "buildtime disable".to_string(),
99 },
100 )
Mårten Kongstad2f954442023-05-17 16:51:16 +0200101 .unwrap()
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000102 .add_flag_value(
103 Source::Memory,
104 FlagValue {
Mårten Kongstad9fb58962023-05-31 13:02:13 +0200105 package: package.to_string(),
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000106 name: "my_flag_one".to_string(),
107 state: FlagState::Disabled,
108 permission: Permission::ReadOnly,
109 },
110 )
Mårten Kongstad2f954442023-05-17 16:51:16 +0200111 .unwrap()
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000112 .add_flag_declaration(
113 Source::File("aconfig_two.txt".to_string()),
114 FlagDeclaration {
115 name: "my_flag_two".to_string(),
Mårten Kongstad066575b2023-06-07 16:29:25 +0200116 namespace: "ns".to_string(),
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000117 description: "buildtime enable".to_string(),
118 },
119 )
Mårten Kongstad2f954442023-05-17 16:51:16 +0200120 .unwrap()
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000121 .add_flag_value(
122 Source::Memory,
123 FlagValue {
Mårten Kongstad9fb58962023-05-31 13:02:13 +0200124 package: package.to_string(),
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000125 name: "my_flag_two".to_string(),
126 state: FlagState::Enabled,
127 permission: Permission::ReadOnly,
128 },
129 )
130 .unwrap();
Mårten Kongstad2f954442023-05-17 16:51:16 +0200131 let cache = builder.build();
Mårten Kongstadfbd71e22023-05-31 13:29:35 +0200132 let expect_content = r#"#ifndef com_example_HEADER_H
133 #define com_example_HEADER_H
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000134
Mårten Kongstadfbd71e22023-05-31 13:29:35 +0200135 namespace com::example {
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000136
Dennis Shen4f78f102023-06-09 18:01:56 +0000137 static const bool my_flag_one() {
138 return false;
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000139 }
140
Dennis Shen4f78f102023-06-09 18:01:56 +0000141 static const bool my_flag_two() {
142 return true;
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000143 }
144
145 }
146 #endif
147 "#;
148 let file = generate_cpp_code(&cache).unwrap();
Mårten Kongstadfbd71e22023-05-31 13:29:35 +0200149 assert_eq!("aconfig/com_example.h", file.path.to_str().unwrap());
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000150 assert_eq!(
151 expect_content.replace(' ', ""),
152 String::from_utf8(file.contents).unwrap().replace(' ', "")
153 );
154 }
155
156 #[test]
157 fn test_cpp_codegen_runtime_flag() {
Mårten Kongstadfbd71e22023-05-31 13:29:35 +0200158 let package = "com.example";
Mårten Kongstad9fb58962023-05-31 13:02:13 +0200159 let mut builder = CacheBuilder::new(package.to_string()).unwrap();
Mårten Kongstad2f954442023-05-17 16:51:16 +0200160 builder
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000161 .add_flag_declaration(
162 Source::File("aconfig_one.txt".to_string()),
163 FlagDeclaration {
164 name: "my_flag_one".to_string(),
Mårten Kongstad066575b2023-06-07 16:29:25 +0200165 namespace: "ns".to_string(),
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000166 description: "buildtime disable".to_string(),
167 },
168 )
Mårten Kongstad2f954442023-05-17 16:51:16 +0200169 .unwrap()
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000170 .add_flag_declaration(
171 Source::File("aconfig_two.txt".to_string()),
172 FlagDeclaration {
173 name: "my_flag_two".to_string(),
Mårten Kongstad066575b2023-06-07 16:29:25 +0200174 namespace: "ns".to_string(),
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000175 description: "runtime enable".to_string(),
176 },
177 )
Mårten Kongstad2f954442023-05-17 16:51:16 +0200178 .unwrap()
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000179 .add_flag_value(
180 Source::Memory,
181 FlagValue {
Mårten Kongstad9fb58962023-05-31 13:02:13 +0200182 package: package.to_string(),
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000183 name: "my_flag_two".to_string(),
184 state: FlagState::Enabled,
185 permission: Permission::ReadWrite,
186 },
187 )
188 .unwrap();
Mårten Kongstad2f954442023-05-17 16:51:16 +0200189 let cache = builder.build();
Mårten Kongstadfbd71e22023-05-31 13:29:35 +0200190 let expect_content = r#"#ifndef com_example_HEADER_H
191 #define com_example_HEADER_H
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000192
193 #include <server_configurable_flags/get_flags.h>
194 using namespace server_configurable_flags;
195
Mårten Kongstadfbd71e22023-05-31 13:29:35 +0200196 namespace com::example {
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000197
Dennis Shen4f78f102023-06-09 18:01:56 +0000198 static const bool my_flag_one() {
199 return GetServerConfigurableFlag(
200 "ns",
201 "com.example.my_flag_one",
202 "false") == "true";
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000203 }
204
Dennis Shen4f78f102023-06-09 18:01:56 +0000205 static const bool my_flag_two() {
206 return GetServerConfigurableFlag(
207 "ns",
208 "com.example.my_flag_two",
209 "true") == "true";
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000210 }
211
212 }
213 #endif
214 "#;
215 let file = generate_cpp_code(&cache).unwrap();
Mårten Kongstadfbd71e22023-05-31 13:29:35 +0200216 assert_eq!("aconfig/com_example.h", file.path.to_str().unwrap());
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000217 assert_eq!(
Mårten Kongstadb0255072023-06-08 10:15:43 +0200218 None,
219 crate::test::first_significant_code_diff(
220 expect_content,
221 &String::from_utf8(file.contents).unwrap()
222 )
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000223 );
224 }
225}