aconfig: use proto struct directly

Remove the hand-crafted wrappers around the structures auto-generated
from protos/aconfig.proto, and use the auto-generated structs directly
intead. This gets rid of a lot of manual repetition, and its inherent
risk.

Also unify how individual fields read from text proto are verified (e.g.
is the flag.name field a valid identifier).

Also change the intermediate cache format from JSON to binary protobuf.

The concept of a 'cache' as an intermediate internal format to represent
parsed input stays. The command line interface still refers to caches.
At the moment a cache file is identical to a parsed_file protbuf, and
the code exploits this internally.

A couple of points regarding the auto-generated structs:

  - Vectors are named in the singular (e.g. parsed_flags.parsed_flag is
    a Vec<ProtoParsedFlag>) because this improves ergonomics for all
    devs working with aconfig input files

  - The auto-generated structs have fields that are of type Option<T>
    and convenience methods (named the same as the fields) to access T

Test: atest aconfig.test aconfig.test.java
Bug: 283910447
Change-Id: I512820cc4bc6c543dea9f6a4356f863120a10be3
diff --git a/tools/aconfig/src/commands.rs b/tools/aconfig/src/commands.rs
index eb860b0..f295697 100644
--- a/tools/aconfig/src/commands.rs
+++ b/tools/aconfig/src/commands.rs
@@ -14,105 +14,160 @@
  * limitations under the License.
  */
 
-use anyhow::{ensure, Context, Result};
+use anyhow::{bail, ensure, Context, Result};
 use clap::ValueEnum;
 use protobuf::Message;
-use serde::{Deserialize, Serialize};
-use std::fmt;
 use std::io::Read;
 use std::path::PathBuf;
 
-use crate::aconfig::{FlagDeclarations, FlagState, FlagValue, Permission};
-use crate::cache::{Cache, CacheBuilder, Item};
 use crate::codegen_cpp::generate_cpp_code;
 use crate::codegen_java::generate_java_code;
 use crate::codegen_rust::generate_rust_code;
-use crate::protos::ProtoParsedFlags;
-
-#[derive(Serialize, Deserialize, Clone, Debug)]
-pub enum Source {
-    #[allow(dead_code)] // only used in unit tests
-    Memory,
-    File(String),
-}
-
-impl fmt::Display for Source {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match self {
-            Self::Memory => write!(f, "<memory>"),
-            Self::File(path) => write!(f, "{}", path),
-        }
-    }
-}
+use crate::protos::{
+    ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag, ProtoParsedFlags, ProtoTracepoint,
+};
 
 pub struct Input {
-    pub source: Source,
+    pub source: String,
     pub reader: Box<dyn Read>,
 }
 
+impl Input {
+    fn try_parse_flags(&mut self) -> Result<ProtoParsedFlags> {
+        let mut buffer = Vec::new();
+        self.reader.read_to_end(&mut buffer)?;
+        crate::protos::parsed_flags::try_from_binary_proto(&buffer)
+    }
+}
+
 pub struct OutputFile {
     pub path: PathBuf, // relative to some root directory only main knows about
     pub contents: Vec<u8>,
 }
 
-pub fn create_cache(package: &str, declarations: Vec<Input>, values: Vec<Input>) -> Result<Cache> {
-    let mut builder = CacheBuilder::new(package.to_owned())?;
+const DEFAULT_FLAG_STATE: ProtoFlagState = ProtoFlagState::DISABLED;
+const DEFAULT_FLAG_PERMISSION: ProtoFlagPermission = ProtoFlagPermission::READ_WRITE;
+
+pub fn parse_flags(package: &str, declarations: Vec<Input>, values: Vec<Input>) -> Result<Vec<u8>> {
+    let mut parsed_flags = ProtoParsedFlags::new();
 
     for mut input in declarations {
         let mut contents = String::new();
         input.reader.read_to_string(&mut contents)?;
-        let dec_list = FlagDeclarations::try_from_text_proto(&contents)
+
+        let flag_declarations = crate::protos::flag_declarations::try_from_text_proto(&contents)
             .with_context(|| format!("Failed to parse {}", input.source))?;
         ensure!(
-            package == dec_list.package,
+            package == flag_declarations.package(),
             "Failed to parse {}: expected package {}, got {}",
             input.source,
             package,
-            dec_list.package
+            flag_declarations.package()
         );
-        for d in dec_list.flags.into_iter() {
-            builder.add_flag_declaration(input.source.clone(), d)?;
+        for mut flag_declaration in flag_declarations.flag.into_iter() {
+            crate::protos::flag_declaration::verify_fields(&flag_declaration)
+                .with_context(|| format!("Failed to parse {}", input.source))?;
+
+            // create ParsedFlag using FlagDeclaration and default values
+            let mut parsed_flag = ProtoParsedFlag::new();
+            parsed_flag.set_package(package.to_string());
+            parsed_flag.set_name(flag_declaration.take_name());
+            parsed_flag.set_namespace(flag_declaration.take_namespace());
+            parsed_flag.set_description(flag_declaration.take_description());
+            parsed_flag.set_state(DEFAULT_FLAG_STATE);
+            parsed_flag.set_permission(DEFAULT_FLAG_PERMISSION);
+            let mut tracepoint = ProtoTracepoint::new();
+            tracepoint.set_source(input.source.clone());
+            tracepoint.set_state(DEFAULT_FLAG_STATE);
+            tracepoint.set_permission(DEFAULT_FLAG_PERMISSION);
+            parsed_flag.trace.push(tracepoint);
+
+            // verify ParsedFlag looks reasonable
+            crate::protos::parsed_flag::verify_fields(&parsed_flag)?;
+
+            // verify ParsedFlag can be added
+            ensure!(
+                parsed_flags.parsed_flag.iter().all(|other| other.name() != parsed_flag.name()),
+                "failed to declare flag {} from {}: flag already declared",
+                parsed_flag.name(),
+                input.source
+            );
+
+            // add ParsedFlag to ParsedFlags
+            parsed_flags.parsed_flag.push(parsed_flag);
         }
     }
 
     for mut input in values {
         let mut contents = String::new();
         input.reader.read_to_string(&mut contents)?;
-        let values_list = FlagValue::try_from_text_proto_list(&contents)
+        let flag_values = crate::protos::flag_values::try_from_text_proto(&contents)
             .with_context(|| format!("Failed to parse {}", input.source))?;
-        for v in values_list {
-            // TODO: warn about flag values that do not take effect?
-            let _ = builder.add_flag_value(input.source.clone(), v);
+        for flag_value in flag_values.flag_value.into_iter() {
+            crate::protos::flag_value::verify_fields(&flag_value)
+                .with_context(|| format!("Failed to parse {}", input.source))?;
+
+            let Some(parsed_flag) = parsed_flags.parsed_flag.iter_mut().find(|pf| pf.package() == flag_value.package() && pf.name() == flag_value.name()) else {
+                // (silently) skip unknown flags
+                continue;
+            };
+
+            parsed_flag.set_state(flag_value.state());
+            parsed_flag.set_permission(flag_value.permission());
+            let mut tracepoint = ProtoTracepoint::new();
+            tracepoint.set_source(input.source.clone());
+            tracepoint.set_state(flag_value.state());
+            tracepoint.set_permission(flag_value.permission());
+            parsed_flag.trace.push(tracepoint);
         }
     }
 
-    Ok(builder.build())
-}
-
-pub fn create_java_lib(cache: Cache) -> Result<Vec<OutputFile>> {
-    generate_java_code(&cache)
-}
-
-pub fn create_cpp_lib(cache: Cache) -> Result<OutputFile> {
-    generate_cpp_code(&cache)
-}
-
-pub fn create_rust_lib(cache: Cache) -> Result<OutputFile> {
-    generate_rust_code(&cache)
-}
-
-pub fn create_device_config_defaults(caches: Vec<Cache>) -> Result<Vec<u8>> {
+    crate::protos::parsed_flags::verify_fields(&parsed_flags)?;
     let mut output = Vec::new();
-    for item in sort_and_iter_items(caches).filter(|item| item.permission == Permission::ReadWrite)
+    parsed_flags.write_to_vec(&mut output)?;
+    Ok(output)
+}
+
+pub fn create_java_lib(mut input: Input) -> Result<Vec<OutputFile>> {
+    let parsed_flags = input.try_parse_flags()?;
+    let Some(package) = find_unique_package(&parsed_flags) else {
+        bail!("no parsed flags, or the parsed flags use different packages");
+    };
+    generate_java_code(package, parsed_flags.parsed_flag.iter())
+}
+
+pub fn create_cpp_lib(mut input: Input) -> Result<OutputFile> {
+    let parsed_flags = input.try_parse_flags()?;
+    let Some(package) = find_unique_package(&parsed_flags) else {
+        bail!("no parsed flags, or the parsed flags use different packages");
+    };
+    generate_cpp_code(package, parsed_flags.parsed_flag.iter())
+}
+
+pub fn create_rust_lib(mut input: Input) -> Result<OutputFile> {
+    let parsed_flags = input.try_parse_flags()?;
+    let Some(package) = find_unique_package(&parsed_flags) else {
+        bail!("no parsed flags, or the parsed flags use different packages");
+    };
+    generate_rust_code(package, parsed_flags.parsed_flag.iter())
+}
+
+pub fn create_device_config_defaults(mut input: Input) -> Result<Vec<u8>> {
+    let parsed_flags = input.try_parse_flags()?;
+    let mut output = Vec::new();
+    for parsed_flag in parsed_flags
+        .parsed_flag
+        .into_iter()
+        .filter(|pf| pf.permission() == ProtoFlagPermission::READ_WRITE)
     {
         let line = format!(
             "{}:{}.{}={}\n",
-            item.namespace,
-            item.package,
-            item.name,
-            match item.state {
-                FlagState::Enabled => "enabled",
-                FlagState::Disabled => "disabled",
+            parsed_flag.namespace(),
+            parsed_flag.package(),
+            parsed_flag.name(),
+            match parsed_flag.state() {
+                ProtoFlagState::ENABLED => "enabled",
+                ProtoFlagState::DISABLED => "disabled",
             }
         );
         output.extend_from_slice(line.as_bytes());
@@ -120,17 +175,21 @@
     Ok(output)
 }
 
-pub fn create_device_config_sysprops(caches: Vec<Cache>) -> Result<Vec<u8>> {
+pub fn create_device_config_sysprops(mut input: Input) -> Result<Vec<u8>> {
+    let parsed_flags = input.try_parse_flags()?;
     let mut output = Vec::new();
-    for item in sort_and_iter_items(caches).filter(|item| item.permission == Permission::ReadWrite)
+    for parsed_flag in parsed_flags
+        .parsed_flag
+        .into_iter()
+        .filter(|pf| pf.permission() == ProtoFlagPermission::READ_WRITE)
     {
         let line = format!(
             "persist.device_config.{}.{}={}\n",
-            item.package,
-            item.name,
-            match item.state {
-                FlagState::Enabled => "true",
-                FlagState::Disabled => "false",
+            parsed_flag.package(),
+            parsed_flag.name(),
+            match parsed_flag.state() {
+                ProtoFlagState::ENABLED => "true",
+                ProtoFlagState::DISABLED => "false",
             }
         );
         output.extend_from_slice(line.as_bytes());
@@ -145,177 +204,118 @@
     Protobuf,
 }
 
-pub fn dump_cache(caches: Vec<Cache>, format: DumpFormat) -> Result<Vec<u8>> {
+pub fn dump_parsed_flags(mut input: Vec<Input>, format: DumpFormat) -> Result<Vec<u8>> {
+    let individually_parsed_flags: Result<Vec<ProtoParsedFlags>> =
+        input.iter_mut().map(|i| i.try_parse_flags()).collect();
+    let parsed_flags: ProtoParsedFlags =
+        crate::protos::parsed_flags::merge(individually_parsed_flags?)?;
+
     let mut output = Vec::new();
     match format {
         DumpFormat::Text => {
-            for item in sort_and_iter_items(caches) {
+            for parsed_flag in parsed_flags.parsed_flag.into_iter() {
                 let line = format!(
                     "{}/{}: {:?} {:?}\n",
-                    item.package, item.name, item.state, item.permission
+                    parsed_flag.package(),
+                    parsed_flag.name(),
+                    parsed_flag.state(),
+                    parsed_flag.permission()
                 );
                 output.extend_from_slice(line.as_bytes());
             }
         }
         DumpFormat::Debug => {
-            for item in sort_and_iter_items(caches) {
-                let line = format!("{:#?}\n", item);
+            for parsed_flag in parsed_flags.parsed_flag.into_iter() {
+                let line = format!("{:#?}\n", parsed_flag);
                 output.extend_from_slice(line.as_bytes());
             }
         }
         DumpFormat::Protobuf => {
-            for cache in sort_and_iter_caches(caches) {
-                let parsed_flags: ProtoParsedFlags = cache.into();
-                parsed_flags.write_to_vec(&mut output)?;
-            }
+            parsed_flags.write_to_vec(&mut output)?;
         }
     }
     Ok(output)
 }
 
-fn sort_and_iter_items(caches: Vec<Cache>) -> impl Iterator<Item = Item> {
-    sort_and_iter_caches(caches).flat_map(|cache| cache.into_iter())
-}
-
-fn sort_and_iter_caches(mut caches: Vec<Cache>) -> impl Iterator<Item = Cache> {
-    caches.sort_by_cached_key(|cache| cache.package().to_string());
-    caches.into_iter()
+fn find_unique_package(parsed_flags: &ProtoParsedFlags) -> Option<&str> {
+    let Some(package) = parsed_flags.parsed_flag.first().map(|pf| pf.package()) else {
+        return None;
+    };
+    if parsed_flags.parsed_flag.iter().any(|pf| pf.package() != package) {
+        return None;
+    }
+    Some(package)
 }
 
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::aconfig::{FlagState, Permission};
-
-    fn create_test_cache_com_example() -> Cache {
-        let s = r#"
-        package: "com.example"
-        flag {
-            name: "a"
-            namespace: "ns"
-            description: "Description of a"
-        }
-        flag {
-            name: "b"
-            namespace: "ns"
-            description: "Description of b"
-        }
-        "#;
-        let declarations = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }];
-        let o = r#"
-        flag_value {
-            package: "com.example"
-            name: "a"
-            state: DISABLED
-            permission: READ_ONLY
-        }
-        "#;
-        let values = vec![Input { source: Source::Memory, reader: Box::new(o.as_bytes()) }];
-        create_cache("com.example", declarations, values).unwrap()
-    }
-
-    fn create_test_cache_com_other() -> Cache {
-        let s = r#"
-        package: "com.other"
-        flag {
-            name: "c"
-            namespace: "ns"
-            description: "Description of c"
-        }
-        "#;
-        let declarations = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }];
-        let o = r#"
-        flag_value {
-            package: "com.other"
-            name: "c"
-            state: DISABLED
-            permission: READ_ONLY
-        }
-        "#;
-        let values = vec![Input { source: Source::Memory, reader: Box::new(o.as_bytes()) }];
-        create_cache("com.other", declarations, values).unwrap()
-    }
 
     #[test]
-    fn test_create_cache() {
-        let caches = create_test_cache_com_example(); // calls create_cache
-        let item = caches.iter().find(|&item| item.name == "a").unwrap();
-        assert_eq!(FlagState::Disabled, item.state);
-        assert_eq!(Permission::ReadOnly, item.permission);
+    fn test_parse_flags() {
+        let parsed_flags = crate::test::parse_test_flags(); // calls parse_flags
+        crate::protos::parsed_flags::verify_fields(&parsed_flags).unwrap();
+
+        let enabled_ro =
+            parsed_flags.parsed_flag.iter().find(|pf| pf.name() == "enabled_ro").unwrap();
+        assert!(crate::protos::parsed_flag::verify_fields(enabled_ro).is_ok());
+        assert_eq!("com.android.aconfig.test", enabled_ro.package());
+        assert_eq!("enabled_ro", enabled_ro.name());
+        assert_eq!("This flag is ENABLED + READ_ONLY", enabled_ro.description());
+        assert_eq!(ProtoFlagState::ENABLED, enabled_ro.state());
+        assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_ro.permission());
+        assert_eq!(3, enabled_ro.trace.len());
+        assert_eq!("tests/test.aconfig", enabled_ro.trace[0].source());
+        assert_eq!(ProtoFlagState::DISABLED, enabled_ro.trace[0].state());
+        assert_eq!(ProtoFlagPermission::READ_WRITE, enabled_ro.trace[0].permission());
+        assert_eq!("tests/first.values", enabled_ro.trace[1].source());
+        assert_eq!(ProtoFlagState::DISABLED, enabled_ro.trace[1].state());
+        assert_eq!(ProtoFlagPermission::READ_WRITE, enabled_ro.trace[1].permission());
+        assert_eq!("tests/second.values", enabled_ro.trace[2].source());
+        assert_eq!(ProtoFlagState::ENABLED, enabled_ro.trace[2].state());
+        assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_ro.trace[2].permission());
+
+        assert_eq!(4, parsed_flags.parsed_flag.len());
+        for pf in parsed_flags.parsed_flag.iter() {
+            let first = pf.trace.first().unwrap();
+            assert_eq!(DEFAULT_FLAG_STATE, first.state());
+            assert_eq!(DEFAULT_FLAG_PERMISSION, first.permission());
+
+            let last = pf.trace.last().unwrap();
+            assert_eq!(pf.state(), last.state());
+            assert_eq!(pf.permission(), last.permission());
+        }
     }
 
     #[test]
     fn test_create_device_config_defaults() {
-        let caches = vec![crate::test::create_cache()];
-        let bytes = create_device_config_defaults(caches).unwrap();
+        let input = parse_test_flags_as_input();
+        let bytes = create_device_config_defaults(input).unwrap();
         let text = std::str::from_utf8(&bytes).unwrap();
         assert_eq!("aconfig_test:com.android.aconfig.test.disabled_rw=disabled\naconfig_test:com.android.aconfig.test.enabled_rw=enabled\n", text);
     }
 
     #[test]
     fn test_create_device_config_sysprops() {
-        let caches = vec![crate::test::create_cache()];
-        let bytes = create_device_config_sysprops(caches).unwrap();
+        let input = parse_test_flags_as_input();
+        let bytes = create_device_config_sysprops(input).unwrap();
         let text = std::str::from_utf8(&bytes).unwrap();
         assert_eq!("persist.device_config.com.android.aconfig.test.disabled_rw=false\npersist.device_config.com.android.aconfig.test.enabled_rw=true\n", text);
     }
 
     #[test]
     fn test_dump_text_format() {
-        let caches = vec![create_test_cache_com_example()];
-        let bytes = dump_cache(caches, DumpFormat::Text).unwrap();
+        let input = parse_test_flags_as_input();
+        let bytes = dump_parsed_flags(vec![input], DumpFormat::Text).unwrap();
         let text = std::str::from_utf8(&bytes).unwrap();
-        assert!(text.contains("a: Disabled"));
+        assert!(text.contains("com.android.aconfig.test/disabled_ro: DISABLED READ_ONLY"));
     }
 
-    #[test]
-    fn test_dump_protobuf_format() {
-        use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoTracepoint};
-        use protobuf::Message;
-
-        let caches = vec![create_test_cache_com_example()];
-        let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap();
-        let actual = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
-
-        assert_eq!(
-            vec!["a".to_string(), "b".to_string()],
-            actual.parsed_flag.iter().map(|item| item.name.clone().unwrap()).collect::<Vec<_>>()
-        );
-
-        let item =
-            actual.parsed_flag.iter().find(|item| item.name == Some("b".to_string())).unwrap();
-        assert_eq!(item.package(), "com.example");
-        assert_eq!(item.name(), "b");
-        assert_eq!(item.description(), "Description of b");
-        assert_eq!(item.state(), ProtoFlagState::DISABLED);
-        assert_eq!(item.permission(), ProtoFlagPermission::READ_WRITE);
-        let mut tp = ProtoTracepoint::new();
-        tp.set_source("<memory>".to_string());
-        tp.set_state(ProtoFlagState::DISABLED);
-        tp.set_permission(ProtoFlagPermission::READ_WRITE);
-        assert_eq!(item.trace, vec![tp]);
-    }
-
-    #[test]
-    fn test_dump_multiple_caches() {
-        let caches = vec![create_test_cache_com_example(), create_test_cache_com_other()];
-        let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap();
-        let dump = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
-        assert_eq!(
-            dump.parsed_flag
-                .iter()
-                .map(|parsed_flag| format!("{}/{}", parsed_flag.package(), parsed_flag.name()))
-                .collect::<Vec<_>>(),
-            vec![
-                "com.example/a".to_string(),
-                "com.example/b".to_string(),
-                "com.other/c".to_string()
-            ]
-        );
-
-        let caches = vec![create_test_cache_com_other(), create_test_cache_com_example()];
-        let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap();
-        let dump_reversed_input = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
-        assert_eq!(dump, dump_reversed_input);
+    fn parse_test_flags_as_input() -> Input {
+        let parsed_flags = crate::test::parse_test_flags();
+        let binary_proto = parsed_flags.write_to_bytes().unwrap();
+        let cursor = std::io::Cursor::new(binary_proto);
+        let reader = Box::new(cursor);
+        Input { source: "test.data".to_string(), reader }
     }
 }