blob: 4f8222080923618f6ce2989680431b4ca0ef8859 [file] [log] [blame]
Zhi Doueb744892023-05-10 04:02:33 +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;
Joe Onorato0c4ef0f2023-05-13 11:30:11 -070019use std::path::PathBuf;
Zhi Doueb744892023-05-10 04:02:33 +000020use tinytemplate::TinyTemplate;
21
22use crate::aconfig::{FlagState, Permission};
23use crate::cache::{Cache, Item};
Mårten Kongstad066575b2023-06-07 16:29:25 +020024use crate::codegen;
Mårten Kongstadd42eeeb2023-05-12 10:01:00 +020025use crate::commands::OutputFile;
Zhi Doueb744892023-05-10 04:02:33 +000026
Zhi Dou4655c962023-06-12 15:56:03 +000027pub fn generate_java_code(cache: &Cache) -> Result<Vec<OutputFile>> {
Mårten Kongstad9fb58962023-05-31 13:02:13 +020028 let package = cache.package();
Mårten Kongstad066575b2023-06-07 16:29:25 +020029 let class_elements: Vec<ClassElement> =
30 cache.iter().map(|item| create_class_element(package, item)).collect();
Zhi Dou4655c962023-06-12 15:56:03 +000031 let is_read_write = class_elements.iter().any(|item| item.is_read_write);
32 let context = Context { package_name: package.to_string(), is_read_write, class_elements };
33
34 let java_files = vec!["Flags.java", "FeatureFlagsImpl.java", "FeatureFlags.java"];
35
Zhi Doueb744892023-05-10 04:02:33 +000036 let mut template = TinyTemplate::new();
Zhi Dou4655c962023-06-12 15:56:03 +000037 template.add_template("Flags.java", include_str!("../templates/Flags.java.template"))?;
38 template.add_template(
39 "FeatureFlagsImpl.java",
40 include_str!("../templates/FeatureFlagsImpl.java.template"),
41 )?;
42 template.add_template(
43 "FeatureFlags.java",
44 include_str!("../templates/FeatureFlags.java.template"),
45 )?;
46
47 let path: PathBuf = package.split('.').collect();
48 java_files
49 .iter()
50 .map(|file| {
51 Ok(OutputFile {
52 contents: template.render(file, &context)?.into(),
53 path: path.join(file),
54 })
55 })
56 .collect::<Result<Vec<OutputFile>>>()
Zhi Doueb744892023-05-10 04:02:33 +000057}
58
59#[derive(Serialize)]
60struct Context {
Zhi Dou4655c962023-06-12 15:56:03 +000061 pub package_name: String,
62 pub is_read_write: bool,
Zhi Doueb744892023-05-10 04:02:33 +000063 pub class_elements: Vec<ClassElement>,
64}
65
66#[derive(Serialize)]
67struct ClassElement {
Zhi Doueb744892023-05-10 04:02:33 +000068 pub default_value: String,
Mårten Kongstad066575b2023-06-07 16:29:25 +020069 pub device_config_namespace: String,
70 pub device_config_flag: String,
Zhi Dou4655c962023-06-12 15:56:03 +000071 pub flag_name_constant_suffix: String,
72 pub is_read_write: bool,
73 pub method_name: String,
Zhi Doueb744892023-05-10 04:02:33 +000074}
75
Mårten Kongstad066575b2023-06-07 16:29:25 +020076fn create_class_element(package: &str, item: &Item) -> ClassElement {
77 let device_config_flag = codegen::create_device_config_ident(package, &item.name)
78 .expect("values checked at cache creation time");
Zhi Doueb744892023-05-10 04:02:33 +000079 ClassElement {
Zhi Doueb744892023-05-10 04:02:33 +000080 default_value: if item.state == FlagState::Enabled {
81 "true".to_string()
82 } else {
83 "false".to_string()
84 },
Mårten Kongstad066575b2023-06-07 16:29:25 +020085 device_config_namespace: item.namespace.clone(),
86 device_config_flag,
Zhi Dou4655c962023-06-12 15:56:03 +000087 flag_name_constant_suffix: item.name.to_ascii_uppercase(),
88 is_read_write: item.permission == Permission::ReadWrite,
Zhi Douaf81e202023-06-14 20:38:20 +000089 method_name: format_java_method_name(&item.name),
Zhi Doueb744892023-05-10 04:02:33 +000090 }
91}
92
Zhi Douaf81e202023-06-14 20:38:20 +000093fn format_java_method_name(flag_name: &str) -> String {
94 flag_name
95 .split('_')
96 .filter(|&word| !word.is_empty())
97 .enumerate()
98 .map(|(index, word)| {
99 if index == 0 {
100 word.to_ascii_lowercase()
101 } else {
102 word[0..1].to_ascii_uppercase() + &word[1..].to_ascii_lowercase()
103 }
104 })
105 .collect::<Vec<String>>()
106 .join("")
107}
108
Zhi Doueb744892023-05-10 04:02:33 +0000109#[cfg(test)]
110mod tests {
111 use super::*;
Zhi Dou4655c962023-06-12 15:56:03 +0000112 use std::collections::HashMap;
Zhi Doueb744892023-05-10 04:02:33 +0000113
114 #[test]
115 fn test_generate_java_code() {
Zhi Dou4655c962023-06-12 15:56:03 +0000116 let cache = crate::test::create_cache();
117 let generated_files = generate_java_code(&cache).unwrap();
118 let expect_flags_content = r#"
119 package com.android.aconfig.test;
Joe Onorato0c4ef0f2023-05-13 11:30:11 -0700120 public final class Flags {
Zhi Douaf81e202023-06-14 20:38:20 +0000121 public static boolean disabledRo() {
122 return FEATURE_FLAGS.disabledRo();
Zhi Doueb744892023-05-10 04:02:33 +0000123 }
Zhi Douaf81e202023-06-14 20:38:20 +0000124 public static boolean disabledRw() {
125 return FEATURE_FLAGS.disabledRw();
Zhi Doueb744892023-05-10 04:02:33 +0000126 }
Zhi Douaf81e202023-06-14 20:38:20 +0000127 public static boolean enabledRo() {
128 return FEATURE_FLAGS.enabledRo();
Zhi Dou4655c962023-06-12 15:56:03 +0000129 }
Zhi Douaf81e202023-06-14 20:38:20 +0000130 public static boolean enabledRw() {
131 return FEATURE_FLAGS.enabledRw();
Zhi Dou4655c962023-06-12 15:56:03 +0000132 }
133 private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
Zhi Doueb744892023-05-10 04:02:33 +0000134
135 }
Mårten Kongstadd42eeeb2023-05-12 10:01:00 +0200136 "#;
Zhi Dou4655c962023-06-12 15:56:03 +0000137 let expected_featureflagsimpl_content = r#"
138 package com.android.aconfig.test;
139 import android.provider.DeviceConfig;
140 public final class FeatureFlagsImpl implements FeatureFlags {
141 @Override
Zhi Douaf81e202023-06-14 20:38:20 +0000142 public boolean disabledRo() {
Zhi Dou4655c962023-06-12 15:56:03 +0000143 return false;
144 }
145 @Override
Zhi Douaf81e202023-06-14 20:38:20 +0000146 public boolean disabledRw() {
Zhi Dou4655c962023-06-12 15:56:03 +0000147 return DeviceConfig.getBoolean(
148 "aconfig_test",
149 "com.android.aconfig.test.disabled_rw",
150 false
151 );
152 }
153 @Override
Zhi Douaf81e202023-06-14 20:38:20 +0000154 public boolean enabledRo() {
Zhi Dou4655c962023-06-12 15:56:03 +0000155 return true;
156 }
157 @Override
Zhi Douaf81e202023-06-14 20:38:20 +0000158 public boolean enabledRw() {
Zhi Dou4655c962023-06-12 15:56:03 +0000159 return DeviceConfig.getBoolean(
160 "aconfig_test",
161 "com.android.aconfig.test.enabled_rw",
162 true
163 );
164 }
165 }
166 "#;
167 let expected_featureflags_content = r#"
168 package com.android.aconfig.test;
169 public interface FeatureFlags {
Zhi Douaf81e202023-06-14 20:38:20 +0000170 boolean disabledRo();
171 boolean disabledRw();
172 boolean enabledRo();
173 boolean enabledRw();
Zhi Dou4655c962023-06-12 15:56:03 +0000174 }
175 "#;
176 let mut file_set = HashMap::from([
177 ("com/android/aconfig/test/Flags.java", expect_flags_content),
178 ("com/android/aconfig/test/FeatureFlagsImpl.java", expected_featureflagsimpl_content),
179 ("com/android/aconfig/test/FeatureFlags.java", expected_featureflags_content),
180 ]);
181
182 for file in generated_files {
183 let file_path = file.path.to_str().unwrap();
184 assert!(file_set.contains_key(file_path), "Cannot find {}", file_path);
185 assert_eq!(
186 None,
187 crate::test::first_significant_code_diff(
188 file_set.get(file_path).unwrap(),
189 &String::from_utf8(file.contents.clone()).unwrap()
190 ),
191 "File {} content is not correct",
192 file_path
193 );
194 file_set.remove(file_path);
195 }
196
197 assert!(file_set.is_empty());
Zhi Doueb744892023-05-10 04:02:33 +0000198 }
Zhi Douaf81e202023-06-14 20:38:20 +0000199
200 #[test]
201 fn test_format_java_method_name() {
202 let input = "____some_snake___name____";
203 let expected = "someSnakeName";
204 let formatted_name = format_java_method_name(input);
205 assert_eq!(expected, formatted_name);
206 }
Zhi Doueb744892023-05-10 04:02:33 +0000207}