blob: d75e315f61d8a55608ed6b5c4aa9c9e68424fc02 [file] [log] [blame]
MÃ¥rten Kongstadf73b9632023-05-24 15:43:47 +02001/*
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_rust_code(cache: &Cache) -> Result<OutputFile> {
26 let namespace = cache.namespace().to_lowercase();
27 let parsed_flags: Vec<TemplateParsedFlag> =
28 cache.iter().map(|item| create_template_parsed_flag(&namespace, item)).collect();
29 let context = TemplateContext { namespace, parsed_flags };
30 let mut template = TinyTemplate::new();
31 template.add_template("rust_code_gen", include_str!("../templates/rust.template"))?;
32 let contents = template.render("rust_code_gen", &context)?;
33 let path = ["src", "lib.rs"].iter().collect();
34 Ok(OutputFile { contents: contents.into(), path })
35}
36
37#[derive(Serialize)]
38struct TemplateContext {
39 pub namespace: String,
40 pub parsed_flags: Vec<TemplateParsedFlag>,
41}
42
43#[derive(Serialize)]
44struct TemplateParsedFlag {
45 pub name: String,
46 pub fn_name: String,
47
48 // TinyTemplate's conditionals are limited to single <bool> expressions; list all options here
49 // Invariant: exactly one of these fields will be true
50 pub is_read_only_enabled: bool,
51 pub is_read_only_disabled: bool,
52 pub is_read_write: bool,
53}
54
55#[allow(clippy::nonminimal_bool)]
56fn create_template_parsed_flag(namespace: &str, item: &Item) -> TemplateParsedFlag {
57 let template = TemplateParsedFlag {
58 name: item.name.clone(),
59 fn_name: format!("{}_{}", namespace, item.name.replace('-', "_").to_lowercase()),
60 is_read_only_enabled: item.permission == Permission::ReadOnly
61 && item.state == FlagState::Enabled,
62 is_read_only_disabled: item.permission == Permission::ReadOnly
63 && item.state == FlagState::Disabled,
64 is_read_write: item.permission == Permission::ReadWrite,
65 };
66 #[rustfmt::skip]
67 debug_assert!(
68 (template.is_read_only_enabled && !template.is_read_only_disabled && !template.is_read_write) ||
69 (!template.is_read_only_enabled && template.is_read_only_disabled && !template.is_read_write) ||
70 (!template.is_read_only_enabled && !template.is_read_only_disabled && template.is_read_write),
71 "TemplateParsedFlag invariant failed: {} {} {}",
72 template.is_read_only_enabled,
73 template.is_read_only_disabled,
74 template.is_read_write,
75 );
76 template
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use crate::commands::{create_cache, Input, Source};
83
84 #[test]
85 fn test_generate_rust_code() {
86 let cache = create_cache(
87 "test",
88 vec![Input {
89 source: Source::File("testdata/test.aconfig".to_string()),
90 reader: Box::new(include_bytes!("../testdata/test.aconfig").as_slice()),
91 }],
92 vec![
93 Input {
94 source: Source::File("testdata/first.values".to_string()),
95 reader: Box::new(include_bytes!("../testdata/first.values").as_slice()),
96 },
97 Input {
98 source: Source::File("testdata/test.aconfig".to_string()),
99 reader: Box::new(include_bytes!("../testdata/second.values").as_slice()),
100 },
101 ],
102 )
103 .unwrap();
104 let generated = generate_rust_code(&cache).unwrap();
105 assert_eq!("src/lib.rs", format!("{}", generated.path.display()));
106 let expected = r#"
107#[inline(always)]
108pub const fn r#test_disabled_ro() -> bool {
109 false
110}
111
112#[inline(always)]
113pub fn r#test_disabled_rw() -> bool {
114 profcollect_libflags_rust::GetServerConfigurableFlag("test", "disabled-rw", "false") == "true"
115}
116
117#[inline(always)]
118pub const fn r#test_enabled_ro() -> bool {
119 true
120}
121
122#[inline(always)]
123pub fn r#test_enabled_rw() -> bool {
124 profcollect_libflags_rust::GetServerConfigurableFlag("test", "enabled-rw", "false") == "true"
125}
126"#;
127 assert_eq!(expected.trim(), String::from_utf8(generated.contents).unwrap().trim());
128 }
129}