blob: df1a17a9f623beb1d9bc225b10216073bf52bcf4 [file] [log] [blame]
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +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
Mårten Kongstad30950782023-05-09 13:31:29 +020017use anyhow::{ensure, Context, Result};
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020018use clap::ValueEnum;
Mårten Kongstada1029092023-05-08 11:51:59 +020019use protobuf::Message;
Mårten Kongstad76adff22023-05-08 10:57:24 +020020use serde::{Deserialize, Serialize};
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020021use std::fmt;
22use std::io::Read;
Mårten Kongstadd42eeeb2023-05-12 10:01:00 +020023use std::path::PathBuf;
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020024
Mårten Kongstadfa23d292023-05-11 14:47:02 +020025use crate::aconfig::{FlagDeclarations, FlagValue};
Mårten Kongstad2f954442023-05-17 16:51:16 +020026use crate::cache::{Cache, CacheBuilder};
Dennis Shen1dc9ad42023-05-12 00:21:55 +000027use crate::codegen_cpp::generate_cpp_code;
Mårten Kongstadd42eeeb2023-05-12 10:01:00 +020028use crate::codegen_java::generate_java_code;
Mårten Kongstadf73b9632023-05-24 15:43:47 +020029use crate::codegen_rust::generate_rust_code;
Mårten Kongstad30950782023-05-09 13:31:29 +020030use crate::protos::ProtoParsedFlags;
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020031
Mårten Kongstad76adff22023-05-08 10:57:24 +020032#[derive(Serialize, Deserialize, Clone, Debug)]
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020033pub enum Source {
34 #[allow(dead_code)] // only used in unit tests
35 Memory,
36 File(String),
37}
38
39impl fmt::Display for Source {
40 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41 match self {
42 Self::Memory => write!(f, "<memory>"),
43 Self::File(path) => write!(f, "{}", path),
44 }
45 }
46}
47
48pub struct Input {
49 pub source: Source,
50 pub reader: Box<dyn Read>,
51}
52
Mårten Kongstadd42eeeb2023-05-12 10:01:00 +020053pub struct OutputFile {
54 pub path: PathBuf, // relative to some root directory only main knows about
55 pub contents: Vec<u8>,
56}
57
Mårten Kongstad30950782023-05-09 13:31:29 +020058pub fn create_cache(
Mårten Kongstad30950782023-05-09 13:31:29 +020059 namespace: &str,
Mårten Kongstadfa23d292023-05-11 14:47:02 +020060 declarations: Vec<Input>,
61 values: Vec<Input>,
Mårten Kongstad30950782023-05-09 13:31:29 +020062) -> Result<Cache> {
Mårten Kongstad2f954442023-05-17 16:51:16 +020063 let mut builder = CacheBuilder::new(namespace.to_owned())?;
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020064
Mårten Kongstadfa23d292023-05-11 14:47:02 +020065 for mut input in declarations {
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020066 let mut contents = String::new();
67 input.reader.read_to_string(&mut contents)?;
Mårten Kongstadfa23d292023-05-11 14:47:02 +020068 let dec_list = FlagDeclarations::try_from_text_proto(&contents)
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020069 .with_context(|| format!("Failed to parse {}", input.source))?;
Mårten Kongstad30950782023-05-09 13:31:29 +020070 ensure!(
Mårten Kongstadfa23d292023-05-11 14:47:02 +020071 namespace == dec_list.namespace,
Mårten Kongstad30950782023-05-09 13:31:29 +020072 "Failed to parse {}: expected namespace {}, got {}",
73 input.source,
74 namespace,
Mårten Kongstadfa23d292023-05-11 14:47:02 +020075 dec_list.namespace
Mårten Kongstad30950782023-05-09 13:31:29 +020076 );
Mårten Kongstadfa23d292023-05-11 14:47:02 +020077 for d in dec_list.flags.into_iter() {
Mårten Kongstad2f954442023-05-17 16:51:16 +020078 builder.add_flag_declaration(input.source.clone(), d)?;
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020079 }
80 }
81
Mårten Kongstadfa23d292023-05-11 14:47:02 +020082 for mut input in values {
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020083 let mut contents = String::new();
84 input.reader.read_to_string(&mut contents)?;
Mårten Kongstadfa23d292023-05-11 14:47:02 +020085 let values_list = FlagValue::try_from_text_proto_list(&contents)
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020086 .with_context(|| format!("Failed to parse {}", input.source))?;
Mårten Kongstadfa23d292023-05-11 14:47:02 +020087 for v in values_list {
88 // TODO: warn about flag values that do not take effect?
Mårten Kongstad2f954442023-05-17 16:51:16 +020089 let _ = builder.add_flag_value(input.source.clone(), v);
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020090 }
91 }
92
Mårten Kongstad2f954442023-05-17 16:51:16 +020093 Ok(builder.build())
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +020094}
95
Mårten Kongstadb27f2ce2023-06-02 11:43:21 +020096pub fn create_java_lib(cache: Cache) -> Result<OutputFile> {
97 generate_java_code(&cache)
Zhi Doueb744892023-05-10 04:02:33 +000098}
99
Mårten Kongstadb27f2ce2023-06-02 11:43:21 +0200100pub fn create_cpp_lib(cache: Cache) -> Result<OutputFile> {
101 generate_cpp_code(&cache)
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000102}
103
Mårten Kongstadb27f2ce2023-06-02 11:43:21 +0200104pub fn create_rust_lib(cache: Cache) -> Result<OutputFile> {
105 generate_rust_code(&cache)
Mårten Kongstadf73b9632023-05-24 15:43:47 +0200106}
107
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200108#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
Mårten Kongstadba94e6a2023-05-16 11:00:16 +0200109pub enum DumpFormat {
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200110 Text,
111 Debug,
Mårten Kongstada1029092023-05-08 11:51:59 +0200112 Protobuf,
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200113}
114
Mårten Kongstadaf677032023-05-17 16:18:25 +0200115pub fn dump_cache(mut caches: Vec<Cache>, format: DumpFormat) -> Result<Vec<u8>> {
116 let mut output = Vec::new();
117 caches.sort_by_cached_key(|cache| cache.namespace().to_string());
118 for cache in caches.into_iter() {
119 match format {
120 DumpFormat::Text => {
121 let mut lines = vec![];
122 for item in cache.iter() {
123 lines.push(format!(
124 "{}/{}: {:?} {:?}\n",
125 item.namespace, item.name, item.state, item.permission
126 ));
127 }
128 output.append(&mut lines.concat().into());
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200129 }
Mårten Kongstadaf677032023-05-17 16:18:25 +0200130 DumpFormat::Debug => {
131 let mut lines = vec![];
132 for item in cache.iter() {
Mårten Kongstad993111f2023-05-24 14:55:11 +0200133 lines.push(format!("{:#?}\n", item));
Mårten Kongstadaf677032023-05-17 16:18:25 +0200134 }
135 output.append(&mut lines.concat().into());
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200136 }
Mårten Kongstadaf677032023-05-17 16:18:25 +0200137 DumpFormat::Protobuf => {
138 let parsed_flags: ProtoParsedFlags = cache.into();
139 parsed_flags.write_to_vec(&mut output)?;
140 }
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200141 }
142 }
Mårten Kongstadaf677032023-05-17 16:18:25 +0200143 Ok(output)
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200149 use crate::aconfig::{FlagState, Permission};
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200150
Mårten Kongstadaf677032023-05-17 16:18:25 +0200151 fn create_test_cache_ns1() -> Cache {
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200152 let s = r#"
Mårten Kongstadaf677032023-05-17 16:18:25 +0200153 namespace: "ns1"
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200154 flag {
Mårten Kongstad30950782023-05-09 13:31:29 +0200155 name: "a"
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200156 description: "Description of a"
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200157 }
Mårten Kongstada1029092023-05-08 11:51:59 +0200158 flag {
Mårten Kongstad30950782023-05-09 13:31:29 +0200159 name: "b"
Mårten Kongstada1029092023-05-08 11:51:59 +0200160 description: "Description of b"
Mårten Kongstada1029092023-05-08 11:51:59 +0200161 }
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200162 "#;
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200163 let declarations = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }];
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200164 let o = r#"
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200165 flag_value {
Mårten Kongstadaf677032023-05-17 16:18:25 +0200166 namespace: "ns1"
Mårten Kongstad30950782023-05-09 13:31:29 +0200167 name: "a"
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200168 state: DISABLED
Mårten Kongstad416330b2023-05-05 11:10:01 +0200169 permission: READ_ONLY
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200170 }
171 "#;
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200172 let values = vec![Input { source: Source::Memory, reader: Box::new(o.as_bytes()) }];
Mårten Kongstadaf677032023-05-17 16:18:25 +0200173 create_cache("ns1", declarations, values).unwrap()
174 }
175
176 fn create_test_cache_ns2() -> Cache {
177 let s = r#"
178 namespace: "ns2"
179 flag {
180 name: "c"
181 description: "Description of c"
182 }
183 "#;
184 let declarations = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }];
185 let o = r#"
186 flag_value {
187 namespace: "ns2"
188 name: "c"
189 state: DISABLED
190 permission: READ_ONLY
191 }
192 "#;
193 let values = vec![Input { source: Source::Memory, reader: Box::new(o.as_bytes()) }];
194 create_cache("ns2", declarations, values).unwrap()
Mårten Kongstada1029092023-05-08 11:51:59 +0200195 }
196
197 #[test]
198 fn test_create_cache() {
Mårten Kongstadaf677032023-05-17 16:18:25 +0200199 let caches = create_test_cache_ns1(); // calls create_cache
200 let item = caches.iter().find(|&item| item.name == "a").unwrap();
Mårten Kongstadc68c4ea2023-05-05 16:20:09 +0200201 assert_eq!(FlagState::Disabled, item.state);
202 assert_eq!(Permission::ReadOnly, item.permission);
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200203 }
Mårten Kongstada1029092023-05-08 11:51:59 +0200204
205 #[test]
206 fn test_dump_text_format() {
Mårten Kongstadaf677032023-05-17 16:18:25 +0200207 let caches = vec![create_test_cache_ns1()];
208 let bytes = dump_cache(caches, DumpFormat::Text).unwrap();
Mårten Kongstada1029092023-05-08 11:51:59 +0200209 let text = std::str::from_utf8(&bytes).unwrap();
210 assert!(text.contains("a: Disabled"));
211 }
212
213 #[test]
214 fn test_dump_protobuf_format() {
Mårten Kongstad30950782023-05-09 13:31:29 +0200215 use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoTracepoint};
Mårten Kongstada1029092023-05-08 11:51:59 +0200216 use protobuf::Message;
217
Mårten Kongstadaf677032023-05-17 16:18:25 +0200218 let caches = vec![create_test_cache_ns1()];
219 let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap();
Mårten Kongstad30950782023-05-09 13:31:29 +0200220 let actual = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
Mårten Kongstada1029092023-05-08 11:51:59 +0200221
222 assert_eq!(
223 vec!["a".to_string(), "b".to_string()],
Mårten Kongstad30950782023-05-09 13:31:29 +0200224 actual.parsed_flag.iter().map(|item| item.name.clone().unwrap()).collect::<Vec<_>>()
Mårten Kongstada1029092023-05-08 11:51:59 +0200225 );
Mårten Kongstad30950782023-05-09 13:31:29 +0200226
227 let item =
228 actual.parsed_flag.iter().find(|item| item.name == Some("b".to_string())).unwrap();
Mårten Kongstadaf677032023-05-17 16:18:25 +0200229 assert_eq!(item.namespace(), "ns1");
Mårten Kongstad30950782023-05-09 13:31:29 +0200230 assert_eq!(item.name(), "b");
231 assert_eq!(item.description(), "Description of b");
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200232 assert_eq!(item.state(), ProtoFlagState::DISABLED);
233 assert_eq!(item.permission(), ProtoFlagPermission::READ_WRITE);
Mårten Kongstad30950782023-05-09 13:31:29 +0200234 let mut tp = ProtoTracepoint::new();
235 tp.set_source("<memory>".to_string());
Mårten Kongstadfa23d292023-05-11 14:47:02 +0200236 tp.set_state(ProtoFlagState::DISABLED);
237 tp.set_permission(ProtoFlagPermission::READ_WRITE);
Mårten Kongstad30950782023-05-09 13:31:29 +0200238 assert_eq!(item.trace, vec![tp]);
Mårten Kongstada1029092023-05-08 11:51:59 +0200239 }
Mårten Kongstadaf677032023-05-17 16:18:25 +0200240
241 #[test]
242 fn test_dump_multiple_caches() {
243 let caches = vec![create_test_cache_ns1(), create_test_cache_ns2()];
244 let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap();
245 let dump = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
246 assert_eq!(
247 dump.parsed_flag
248 .iter()
249 .map(|parsed_flag| format!("{}/{}", parsed_flag.namespace(), parsed_flag.name()))
250 .collect::<Vec<_>>(),
251 vec!["ns1/a".to_string(), "ns1/b".to_string(), "ns2/c".to_string()]
252 );
253
254 let caches = vec![create_test_cache_ns2(), create_test_cache_ns1()];
255 let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap();
256 let dump_reversed_input = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
257 assert_eq!(dump, dump_reversed_input);
258 }
Mårten Kongstad4d2b4b02023-04-27 16:05:58 +0200259}