blob: 54fa0dce332a22ef6ee3b5b5f27dc3fafdda5ed4 [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,
89 method_name: item.name.clone(),
Zhi Doueb744892023-05-10 04:02:33 +000090 }
91}
92
Zhi Doueb744892023-05-10 04:02:33 +000093#[cfg(test)]
94mod tests {
95 use super::*;
Zhi Dou4655c962023-06-12 15:56:03 +000096 use std::collections::HashMap;
Zhi Doueb744892023-05-10 04:02:33 +000097
98 #[test]
99 fn test_generate_java_code() {
Zhi Dou4655c962023-06-12 15:56:03 +0000100 let cache = crate::test::create_cache();
101 let generated_files = generate_java_code(&cache).unwrap();
102 let expect_flags_content = r#"
103 package com.android.aconfig.test;
Joe Onorato0c4ef0f2023-05-13 11:30:11 -0700104 public final class Flags {
Zhi Dou4655c962023-06-12 15:56:03 +0000105 public static boolean disabled_ro() {
106 return FEATURE_FLAGS.disabled_ro();
Zhi Doueb744892023-05-10 04:02:33 +0000107 }
Zhi Dou4655c962023-06-12 15:56:03 +0000108 public static boolean disabled_rw() {
109 return FEATURE_FLAGS.disabled_rw();
Zhi Doueb744892023-05-10 04:02:33 +0000110 }
Zhi Dou4655c962023-06-12 15:56:03 +0000111 public static boolean enabled_ro() {
112 return FEATURE_FLAGS.enabled_ro();
113 }
114 public static boolean enabled_rw() {
115 return FEATURE_FLAGS.enabled_rw();
116 }
117 private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
Zhi Doueb744892023-05-10 04:02:33 +0000118
119 }
Mårten Kongstadd42eeeb2023-05-12 10:01:00 +0200120 "#;
Zhi Dou4655c962023-06-12 15:56:03 +0000121 let expected_featureflagsimpl_content = r#"
122 package com.android.aconfig.test;
123 import android.provider.DeviceConfig;
124 public final class FeatureFlagsImpl implements FeatureFlags {
125 @Override
126 public boolean disabled_ro() {
127 return false;
128 }
129 @Override
130 public boolean disabled_rw() {
131 return DeviceConfig.getBoolean(
132 "aconfig_test",
133 "com.android.aconfig.test.disabled_rw",
134 false
135 );
136 }
137 @Override
138 public boolean enabled_ro() {
139 return true;
140 }
141 @Override
142 public boolean enabled_rw() {
143 return DeviceConfig.getBoolean(
144 "aconfig_test",
145 "com.android.aconfig.test.enabled_rw",
146 true
147 );
148 }
149 }
150 "#;
151 let expected_featureflags_content = r#"
152 package com.android.aconfig.test;
153 public interface FeatureFlags {
154 boolean disabled_ro();
155 boolean disabled_rw();
156 boolean enabled_ro();
157 boolean enabled_rw();
158 }
159 "#;
160 let mut file_set = HashMap::from([
161 ("com/android/aconfig/test/Flags.java", expect_flags_content),
162 ("com/android/aconfig/test/FeatureFlagsImpl.java", expected_featureflagsimpl_content),
163 ("com/android/aconfig/test/FeatureFlags.java", expected_featureflags_content),
164 ]);
165
166 for file in generated_files {
167 let file_path = file.path.to_str().unwrap();
168 assert!(file_set.contains_key(file_path), "Cannot find {}", file_path);
169 assert_eq!(
170 None,
171 crate::test::first_significant_code_diff(
172 file_set.get(file_path).unwrap(),
173 &String::from_utf8(file.contents.clone()).unwrap()
174 ),
175 "File {} content is not correct",
176 file_path
177 );
178 file_set.remove(file_path);
179 }
180
181 assert!(file_set.is_empty());
Zhi Doueb744892023-05-10 04:02:33 +0000182 }
183}