blob: bbf1272aefebe3b3b8c89976f262051f87728af2 [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;
19use tinytemplate::TinyTemplate;
20
21use crate::aconfig::{FlagState, Permission};
22use crate::cache::{Cache, Item};
Mårten Kongstadd42eeeb2023-05-12 10:01:00 +020023use crate::commands::OutputFile;
Zhi Doueb744892023-05-10 04:02:33 +000024
Mårten Kongstadd42eeeb2023-05-12 10:01:00 +020025pub fn generate_java_code(cache: &Cache) -> Result<OutputFile> {
Zhi Doueb744892023-05-10 04:02:33 +000026 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 = uppercase_first_letter(
29 cache.iter().find(|item| !item.namespace.is_empty()).unwrap().namespace.as_str(),
30 );
31 let context = Context { namespace: namespace.clone(), readwrite, class_elements };
32 let mut template = TinyTemplate::new();
33 template.add_template("java_code_gen", include_str!("../templates/java.template"))?;
Mårten Kongstadd42eeeb2023-05-12 10:01:00 +020034 let contents = template.render("java_code_gen", &context)?;
35 let path = ["com", "android", "internal", "aconfig", &(namespace + ".java")].iter().collect();
36 Ok(OutputFile { contents: contents.into(), path })
Zhi Doueb744892023-05-10 04:02:33 +000037}
38
39#[derive(Serialize)]
40struct Context {
41 pub namespace: String,
42 pub readwrite: bool,
43 pub class_elements: Vec<ClassElement>,
44}
45
46#[derive(Serialize)]
47struct ClassElement {
48 pub method_name: String,
49 pub readwrite: bool,
50 pub default_value: String,
51 pub feature_name: String,
52 pub flag_name: String,
53}
54
55fn create_class_element(item: &Item) -> ClassElement {
56 ClassElement {
57 method_name: item.name.clone(),
58 readwrite: item.permission == Permission::ReadWrite,
59 default_value: if item.state == FlagState::Enabled {
60 "true".to_string()
61 } else {
62 "false".to_string()
63 },
64 feature_name: item.name.clone(),
65 flag_name: item.name.clone(),
66 }
67}
68
69fn uppercase_first_letter(s: &str) -> String {
70 s.chars()
71 .enumerate()
72 .map(
73 |(index, ch)| {
74 if index == 0 {
75 ch.to_ascii_uppercase()
76 } else {
77 ch.to_ascii_lowercase()
78 }
79 },
80 )
81 .collect()
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
Mårten Kongstadfa23d292023-05-11 14:47:02 +020087 use crate::aconfig::{FlagDeclaration, FlagValue};
Zhi Doueb744892023-05-10 04:02:33 +000088 use crate::commands::Source;
89
90 #[test]
91 fn test_generate_java_code() {
92 let namespace = "TeSTFlaG";
Mårten Kongstadfa23d292023-05-11 14:47:02 +020093 let mut cache = Cache::new(namespace.to_string());
Zhi Doueb744892023-05-10 04:02:33 +000094 cache
Mårten Kongstadfa23d292023-05-11 14:47:02 +020095 .add_flag_declaration(
Zhi Doueb744892023-05-10 04:02:33 +000096 Source::File("test.txt".to_string()),
Mårten Kongstadfa23d292023-05-11 14:47:02 +020097 FlagDeclaration {
Zhi Doueb744892023-05-10 04:02:33 +000098 name: "test".to_string(),
99 description: "buildtime enable".to_string(),
Zhi Doueb744892023-05-10 04:02:33 +0000100 },
101 )
102 .unwrap();
103 cache
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200104 .add_flag_declaration(
Zhi Doueb744892023-05-10 04:02:33 +0000105 Source::File("test2.txt".to_string()),
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200106 FlagDeclaration {
Zhi Doueb744892023-05-10 04:02:33 +0000107 name: "test2".to_string(),
108 description: "runtime disable".to_string(),
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200109 },
110 )
111 .unwrap();
112 cache
113 .add_flag_value(
114 Source::Memory,
115 FlagValue {
116 namespace: namespace.to_string(),
117 name: "test".to_string(),
118 state: FlagState::Disabled,
119 permission: Permission::ReadOnly,
Zhi Doueb744892023-05-10 04:02:33 +0000120 },
121 )
122 .unwrap();
Mårten Kongstadd42eeeb2023-05-12 10:01:00 +0200123 let expect_content = r#"package com.android.internal.aconfig;
Zhi Doueb744892023-05-10 04:02:33 +0000124
125 import android.provider.DeviceConfig;
126
127 public final class Testflag {
128
129 public static boolean test() {
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200130 return false;
Zhi Doueb744892023-05-10 04:02:33 +0000131 }
132
133 public static boolean test2() {
134 return DeviceConfig.getBoolean(
Mårten Kongstadd42eeeb2023-05-12 10:01:00 +0200135 "Testflag",
136 "test2__test2",
Zhi Doueb744892023-05-10 04:02:33 +0000137 false
138 );
139 }
140
141 }
Mårten Kongstadd42eeeb2023-05-12 10:01:00 +0200142 "#;
143 let file = generate_java_code(&cache).unwrap();
144 assert_eq!("com/android/internal/aconfig/Testflag.java", file.path.to_str().unwrap());
145 assert_eq!(
146 expect_content.replace(' ', ""),
147 String::from_utf8(file.contents).unwrap().replace(' ', "")
148 );
Zhi Doueb744892023-05-10 04:02:33 +0000149 }
150}