aconfig: support custom `dump format` specs

Teach `dump --format=<arg>` to format the output according to a
user-defined format string. The format string now accepts these
arguments:

  - "protobuf": output all data as binary protobuf (as before)
  - "textproto": output all data as text protobuf (as before)
  - any other string: format according to the format spec, see below

Custom format spec: placeholders, enclosed in { and } and named after
the fields of ProtoParsedFlag, will be replaced by the actual values.
All other text is output verbatim. As an example:

  - "{name}={state}" -> "enabled_ro=ENABLED"

Some fields support an alternative formatting via {<field>:<format>}. As
an example:

  - "{name}={state:bool}" -> "enabled_ro=true"

Note that the text replacement does not support escaping { and }. This
means there is no way to print the string "{name}" without expanding it
to the actual flag's name. If needed this feature can be introduced in a
later CL.

For backwards compatibility, the following format strings have special
meaning and will produce an output identically to what it was before
this change:

  - "text"
  - "verbose"
  - "bool"

A follow-up CL will add a new `dump --filter=` argument to limit which
parsed flags are included in the output.

Test: atest
Bug: b/315487153
Change-Id: If7c14b5fb3e7b41ea962425078bd04b4996318f4
diff --git a/tools/aconfig/src/main.rs b/tools/aconfig/src/main.rs
index 63a50c8..08e8b97 100644
--- a/tools/aconfig/src/main.rs
+++ b/tools/aconfig/src/main.rs
@@ -26,13 +26,16 @@
 
 mod codegen;
 mod commands;
+mod dump;
 mod protos;
 mod storage;
 
+use dump::DumpFormat;
+
 #[cfg(test)]
 mod test;
 
-use commands::{CodegenMode, DumpFormat, Input, OutputFile};
+use commands::{CodegenMode, Input, OutputFile};
 
 fn cli() -> Command {
     Command::new("aconfig")
@@ -103,7 +106,7 @@
                 .arg(
                     Arg::new("format")
                         .long("format")
-                        .value_parser(EnumValueParser::<commands::DumpFormat>::new())
+                        .value_parser(|s: &str| DumpFormat::try_from(s))
                         .default_value("text"),
                 )
                 .arg(Arg::new("dedup").long("dedup").num_args(0).action(ArgAction::SetTrue))
@@ -250,7 +253,7 @@
             let format = get_required_arg::<DumpFormat>(sub_matches, "format")
                 .context("failed to dump previously parsed flags")?;
             let dedup = get_required_arg::<bool>(sub_matches, "dedup")?;
-            let output = commands::dump_parsed_flags(input, *format, *dedup)?;
+            let output = commands::dump_parsed_flags(input, format.clone(), *dedup)?;
             let path = get_required_arg::<String>(sub_matches, "out")?;
             write_output_to_file_or_stdout(path, &output)?;
         }