blob: 2aeea6a7c2a7cd767c26ea394f6dfbc557e035da [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
17use anyhow::Result;
18use serde::Serialize;
19use tinytemplate::TinyTemplate;
20
21use crate::aconfig::{FlagState, Permission};
22use crate::cache::{Cache, Item};
23use crate::commands::OutputFile;
24
25pub fn generate_cpp_code(cache: &Cache) -> Result<OutputFile> {
26 let class_elements: Vec<ClassElement> = cache.iter().map(create_class_element).collect();
27 let readwrite = class_elements.iter().any(|item| item.readwrite);
28 let namespace = cache.namespace().to_lowercase();
29 let context = Context { namespace: namespace.clone(), readwrite, class_elements };
30 let mut template = TinyTemplate::new();
31 template.add_template("cpp_code_gen", include_str!("../templates/cpp.template"))?;
32 let contents = template.render("cpp_code_gen", &context)?;
33 let path = ["aconfig", &(namespace + ".h")].iter().collect();
34 Ok(OutputFile { contents: contents.into(), path })
35}
36
37#[derive(Serialize)]
38struct Context {
39 pub namespace: String,
40 pub readwrite: bool,
41 pub class_elements: Vec<ClassElement>,
42}
43
44#[derive(Serialize)]
45struct ClassElement {
46 pub readwrite: bool,
47 pub default_value: String,
48 pub flag_name: String,
49}
50
51fn create_class_element(item: &Item) -> ClassElement {
52 ClassElement {
53 readwrite: item.permission == Permission::ReadWrite,
54 default_value: if item.state == FlagState::Enabled {
55 "true".to_string()
56 } else {
57 "false".to_string()
58 },
59 flag_name: item.name.clone(),
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66 use crate::aconfig::{FlagDeclaration, FlagState, FlagValue, Permission};
Mårten Kongstad2f954442023-05-17 16:51:16 +020067 use crate::cache::CacheBuilder;
Dennis Shen1dc9ad42023-05-12 00:21:55 +000068 use crate::commands::Source;
69
70 #[test]
71 fn test_cpp_codegen_build_time_flag_only() {
72 let namespace = "my_namespace";
Mårten Kongstad2f954442023-05-17 16:51:16 +020073 let mut builder = CacheBuilder::new(namespace.to_string()).unwrap();
74 builder
Dennis Shen1dc9ad42023-05-12 00:21:55 +000075 .add_flag_declaration(
76 Source::File("aconfig_one.txt".to_string()),
77 FlagDeclaration {
78 name: "my_flag_one".to_string(),
79 description: "buildtime disable".to_string(),
80 },
81 )
Mårten Kongstad2f954442023-05-17 16:51:16 +020082 .unwrap()
Dennis Shen1dc9ad42023-05-12 00:21:55 +000083 .add_flag_value(
84 Source::Memory,
85 FlagValue {
86 namespace: namespace.to_string(),
87 name: "my_flag_one".to_string(),
88 state: FlagState::Disabled,
89 permission: Permission::ReadOnly,
90 },
91 )
Mårten Kongstad2f954442023-05-17 16:51:16 +020092 .unwrap()
Dennis Shen1dc9ad42023-05-12 00:21:55 +000093 .add_flag_declaration(
94 Source::File("aconfig_two.txt".to_string()),
95 FlagDeclaration {
96 name: "my_flag_two".to_string(),
97 description: "buildtime enable".to_string(),
98 },
99 )
Mårten Kongstad2f954442023-05-17 16:51:16 +0200100 .unwrap()
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000101 .add_flag_value(
102 Source::Memory,
103 FlagValue {
104 namespace: namespace.to_string(),
105 name: "my_flag_two".to_string(),
106 state: FlagState::Enabled,
107 permission: Permission::ReadOnly,
108 },
109 )
110 .unwrap();
Mårten Kongstad2f954442023-05-17 16:51:16 +0200111 let cache = builder.build();
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000112 let expect_content = r#"#ifndef my_namespace_HEADER_H
113 #define my_namespace_HEADER_H
114 #include "my_namespace.h"
115
116 namespace my_namespace {
117
118 class my_flag_one {
119 public:
120 virtual const bool value() {
121 return false;
122 }
123 }
124
125 class my_flag_two {
126 public:
127 virtual const bool value() {
128 return true;
129 }
130 }
131
132 }
133 #endif
134 "#;
135 let file = generate_cpp_code(&cache).unwrap();
136 assert_eq!("aconfig/my_namespace.h", file.path.to_str().unwrap());
137 assert_eq!(
138 expect_content.replace(' ', ""),
139 String::from_utf8(file.contents).unwrap().replace(' ', "")
140 );
141 }
142
143 #[test]
144 fn test_cpp_codegen_runtime_flag() {
145 let namespace = "my_namespace";
Mårten Kongstad2f954442023-05-17 16:51:16 +0200146 let mut builder = CacheBuilder::new(namespace.to_string()).unwrap();
147 builder
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000148 .add_flag_declaration(
149 Source::File("aconfig_one.txt".to_string()),
150 FlagDeclaration {
151 name: "my_flag_one".to_string(),
152 description: "buildtime disable".to_string(),
153 },
154 )
Mårten Kongstad2f954442023-05-17 16:51:16 +0200155 .unwrap()
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000156 .add_flag_declaration(
157 Source::File("aconfig_two.txt".to_string()),
158 FlagDeclaration {
159 name: "my_flag_two".to_string(),
160 description: "runtime enable".to_string(),
161 },
162 )
Mårten Kongstad2f954442023-05-17 16:51:16 +0200163 .unwrap()
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000164 .add_flag_value(
165 Source::Memory,
166 FlagValue {
167 namespace: namespace.to_string(),
168 name: "my_flag_two".to_string(),
169 state: FlagState::Enabled,
170 permission: Permission::ReadWrite,
171 },
172 )
173 .unwrap();
Mårten Kongstad2f954442023-05-17 16:51:16 +0200174 let cache = builder.build();
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000175 let expect_content = r#"#ifndef my_namespace_HEADER_H
176 #define my_namespace_HEADER_H
177 #include "my_namespace.h"
178
179 #include <server_configurable_flags/get_flags.h>
180 using namespace server_configurable_flags;
181
182 namespace my_namespace {
183
184 class my_flag_one {
185 public:
186 virtual const bool value() {
187 return GetServerConfigurableFlag(
188 "my_namespace",
189 "my_flag_one",
190 "false") == "true";
191 }
192 }
193
194 class my_flag_two {
195 public:
196 virtual const bool value() {
197 return GetServerConfigurableFlag(
198 "my_namespace",
199 "my_flag_two",
200 "true") == "true";
201 }
202 }
203
204 }
205 #endif
206 "#;
207 let file = generate_cpp_code(&cache).unwrap();
208 assert_eq!("aconfig/my_namespace.h", file.path.to_str().unwrap());
209 assert_eq!(
210 expect_content.replace(' ', ""),
211 String::from_utf8(file.contents).unwrap().replace(' ', "")
212 );
213 }
214}