Merge changes from topic "aconfig-part-4"
* changes:
aconfig: introduce namespace, rename proto messages
aconfig: add dump protobuf format
diff --git a/tools/aconfig/protos/aconfig.proto b/tools/aconfig/protos/aconfig.proto
index 6eac414..a3b1fec 100644
--- a/tools/aconfig/protos/aconfig.proto
+++ b/tools/aconfig/protos/aconfig.proto
@@ -20,38 +20,66 @@
package android.aconfig;
+// messages used in both aconfig input and output
+
enum flag_state {
ENABLED = 1;
DISABLED = 2;
}
-enum permission {
+enum flag_permission {
READ_ONLY = 1;
READ_WRITE = 2;
}
-message value {
+// aconfig input messages: configuration and override data
+
+message flag_value {
required flag_state state = 1;
- required permission permission = 2;
+ required flag_permission permission = 2;
optional uint32 since = 3;
}
-message flag {
- required string id = 1;
+message flag_definition {
+ required string name = 1;
required string description = 2;
- repeated value value = 3;
+ repeated flag_value value = 3;
};
-message android_config {
- repeated flag flag = 1;
+message namespace {
+ required string namespace = 1;
+ repeated flag_definition flag = 2;
};
-message override {
- required string id = 1;
+message flag_override {
+ required string namespace = 1;
+ required string name = 2;
+ required flag_state state = 3;
+ required flag_permission permission = 4;
+};
+
+message flag_overrides {
+ repeated flag_override flag_override = 1;
+};
+
+// aconfig output messages: parsed and verified configuration and override data
+
+message tracepoint {
+ // path to config or override file releative to $TOP
+ required string source = 1;
required flag_state state = 2;
- required permission permission = 3;
-};
+ required flag_permission permission = 3;
+}
-message override_config {
- repeated override override = 1;
-};
+message parsed_flag {
+ required string namespace = 1;
+ required string name = 2;
+ required string description = 3;
+ required flag_state state = 4;
+ required flag_permission permission = 5;
+ repeated tracepoint trace = 6;
+}
+
+message parsed_flags {
+ repeated parsed_flag parsed_flag = 1;
+}
diff --git a/tools/aconfig/src/aconfig.rs b/tools/aconfig/src/aconfig.rs
index f10ca1f..8fe82b6 100644
--- a/tools/aconfig/src/aconfig.rs
+++ b/tools/aconfig/src/aconfig.rs
@@ -18,9 +18,11 @@
use protobuf::{Enum, EnumOrUnknown};
use serde::{Deserialize, Serialize};
+use crate::cache::{Cache, Item, Tracepoint};
use crate::protos::{
- ProtoAndroidConfig, ProtoFlag, ProtoFlagState, ProtoOverride, ProtoOverrideConfig,
- ProtoPermission, ProtoValue,
+ ProtoFlagDefinition, ProtoFlagDefinitionValue, ProtoFlagOverride, ProtoFlagOverrides,
+ ProtoFlagPermission, ProtoFlagState, ProtoNamespace, ProtoParsedFlag, ProtoParsedFlags,
+ ProtoTracepoint,
};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
@@ -41,24 +43,42 @@
}
}
+impl From<FlagState> for ProtoFlagState {
+ fn from(state: FlagState) -> Self {
+ match state {
+ FlagState::Enabled => ProtoFlagState::ENABLED,
+ FlagState::Disabled => ProtoFlagState::DISABLED,
+ }
+ }
+}
+
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
pub enum Permission {
ReadOnly,
ReadWrite,
}
-impl TryFrom<EnumOrUnknown<ProtoPermission>> for Permission {
+impl TryFrom<EnumOrUnknown<ProtoFlagPermission>> for Permission {
type Error = Error;
- fn try_from(proto: EnumOrUnknown<ProtoPermission>) -> Result<Self, Self::Error> {
- match ProtoPermission::from_i32(proto.value()) {
- Some(ProtoPermission::READ_ONLY) => Ok(Permission::ReadOnly),
- Some(ProtoPermission::READ_WRITE) => Ok(Permission::ReadWrite),
+ fn try_from(proto: EnumOrUnknown<ProtoFlagPermission>) -> Result<Self, Self::Error> {
+ match ProtoFlagPermission::from_i32(proto.value()) {
+ Some(ProtoFlagPermission::READ_ONLY) => Ok(Permission::ReadOnly),
+ Some(ProtoFlagPermission::READ_WRITE) => Ok(Permission::ReadWrite),
None => Err(anyhow!("unknown permission enum value {}", proto.value())),
}
}
}
+impl From<Permission> for ProtoFlagPermission {
+ fn from(permission: Permission) -> Self {
+ match permission {
+ Permission::ReadOnly => ProtoFlagPermission::READ_ONLY,
+ Permission::ReadWrite => ProtoFlagPermission::READ_WRITE,
+ }
+ }
+}
+
#[derive(Debug, PartialEq, Eq)]
pub struct Value {
state: FlagState,
@@ -77,10 +97,10 @@
}
}
-impl TryFrom<ProtoValue> for Value {
+impl TryFrom<ProtoFlagDefinitionValue> for Value {
type Error = Error;
- fn try_from(proto: ProtoValue) -> Result<Self, Self::Error> {
+ fn try_from(proto: ProtoFlagDefinitionValue) -> Result<Self, Self::Error> {
let Some(proto_state) = proto.state else {
return Err(anyhow!("missing 'state' field"));
};
@@ -95,7 +115,7 @@
#[derive(Debug, PartialEq, Eq)]
pub struct Flag {
- pub id: String,
+ pub name: String,
pub description: String,
// ordered by Value.since; guaranteed to contain at least one item (the default value, with
@@ -106,17 +126,11 @@
impl Flag {
#[allow(dead_code)] // only used in unit tests
pub fn try_from_text_proto(text_proto: &str) -> Result<Flag> {
- let proto: ProtoFlag = crate::protos::try_from_text_proto(text_proto)
+ let proto: ProtoFlagDefinition = crate::protos::try_from_text_proto(text_proto)
.with_context(|| text_proto.to_owned())?;
proto.try_into()
}
- pub fn try_from_text_proto_list(text_proto: &str) -> Result<Vec<Flag>> {
- let proto: ProtoAndroidConfig = crate::protos::try_from_text_proto(text_proto)
- .with_context(|| text_proto.to_owned())?;
- proto.flag.into_iter().map(|proto_flag| proto_flag.try_into()).collect()
- }
-
pub fn resolve(&self, build_id: u32) -> (FlagState, Permission) {
let mut state = self.values[0].state;
let mut permission = self.values[0].permission;
@@ -131,12 +145,12 @@
}
}
-impl TryFrom<ProtoFlag> for Flag {
+impl TryFrom<ProtoFlagDefinition> for Flag {
type Error = Error;
- fn try_from(proto: ProtoFlag) -> Result<Self, Self::Error> {
- let Some(id) = proto.id else {
- return Err(anyhow!("missing 'id' field"));
+ fn try_from(proto: ProtoFlagDefinition) -> Result<Self, Self::Error> {
+ let Some(name) = proto.name else {
+ return Err(anyhow!("missing 'name' field"));
};
let Some(description) = proto.description else {
return Err(anyhow!("missing 'description' field"));
@@ -150,8 +164,8 @@
let v: Value = proto_value.try_into()?;
if values.iter().any(|w| v.since == w.since) {
let msg = match v.since {
- None => format!("flag {}: multiple default values", id),
- Some(x) => format!("flag {}: multiple values for since={}", id, x),
+ None => format!("flag {}: multiple default values", name),
+ Some(x) => format!("flag {}: multiple values for since={}", name, x),
};
return Err(anyhow!(msg));
}
@@ -159,13 +173,35 @@
}
values.sort_by_key(|v| v.since);
- Ok(Flag { id, description, values })
+ Ok(Flag { name, description, values })
+ }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct Namespace {
+ pub namespace: String,
+ pub flags: Vec<Flag>,
+}
+
+impl Namespace {
+ pub fn try_from_text_proto(text_proto: &str) -> Result<Namespace> {
+ let proto: ProtoNamespace = crate::protos::try_from_text_proto(text_proto)
+ .with_context(|| text_proto.to_owned())?;
+ let Some(namespace) = proto.namespace else {
+ return Err(anyhow!("missing 'namespace' field"));
+ };
+ let mut flags = vec![];
+ for proto_flag in proto.flag.into_iter() {
+ flags.push(proto_flag.try_into()?);
+ }
+ Ok(Namespace { namespace, flags })
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct Override {
- pub id: String,
+ pub namespace: String,
+ pub name: String,
pub state: FlagState,
pub permission: Permission,
}
@@ -173,22 +209,25 @@
impl Override {
#[allow(dead_code)] // only used in unit tests
pub fn try_from_text_proto(text_proto: &str) -> Result<Override> {
- let proto: ProtoOverride = crate::protos::try_from_text_proto(text_proto)?;
+ let proto: ProtoFlagOverride = crate::protos::try_from_text_proto(text_proto)?;
proto.try_into()
}
pub fn try_from_text_proto_list(text_proto: &str) -> Result<Vec<Override>> {
- let proto: ProtoOverrideConfig = crate::protos::try_from_text_proto(text_proto)?;
- proto.override_.into_iter().map(|proto_flag| proto_flag.try_into()).collect()
+ let proto: ProtoFlagOverrides = crate::protos::try_from_text_proto(text_proto)?;
+ proto.flag_override.into_iter().map(|proto_flag| proto_flag.try_into()).collect()
}
}
-impl TryFrom<ProtoOverride> for Override {
+impl TryFrom<ProtoFlagOverride> for Override {
type Error = Error;
- fn try_from(proto: ProtoOverride) -> Result<Self, Self::Error> {
- let Some(id) = proto.id else {
- return Err(anyhow!("missing 'id' field"));
+ fn try_from(proto: ProtoFlagOverride) -> Result<Self, Self::Error> {
+ let Some(namespace) = proto.namespace else {
+ return Err(anyhow!("missing 'namespace' field"));
+ };
+ let Some(name) = proto.name else {
+ return Err(anyhow!("missing 'name' field"));
};
let Some(proto_state) = proto.state else {
return Err(anyhow!("missing 'state' field"));
@@ -198,7 +237,42 @@
return Err(anyhow!("missing 'permission' field"));
};
let permission = proto_permission.try_into()?;
- Ok(Override { id, state, permission })
+ Ok(Override { namespace, name, state, permission })
+ }
+}
+
+impl From<Cache> for ProtoParsedFlags {
+ fn from(cache: Cache) -> Self {
+ let mut proto = ProtoParsedFlags::new();
+ for item in cache.into_iter() {
+ proto.parsed_flag.push(item.into());
+ }
+ proto
+ }
+}
+
+impl From<Item> for ProtoParsedFlag {
+ fn from(item: Item) -> Self {
+ let mut proto = crate::protos::ProtoParsedFlag::new();
+ proto.set_namespace(item.namespace.to_owned());
+ proto.set_name(item.name.clone());
+ proto.set_description(item.description.clone());
+ proto.set_state(item.state.into());
+ proto.set_permission(item.permission.into());
+ for trace in item.trace.into_iter() {
+ proto.trace.push(trace.into());
+ }
+ proto
+ }
+}
+
+impl From<Tracepoint> for ProtoTracepoint {
+ fn from(tracepoint: Tracepoint) -> Self {
+ let mut proto = ProtoTracepoint::new();
+ proto.set_source(format!("{}", tracepoint.source));
+ proto.set_state(tracepoint.state.into());
+ proto.set_permission(tracepoint.permission.into());
+ proto
}
}
@@ -209,7 +283,7 @@
#[test]
fn test_flag_try_from_text_proto() {
let expected = Flag {
- id: "1234".to_owned(),
+ name: "1234".to_owned(),
description: "Description of the flag".to_owned(),
values: vec![
Value::default(FlagState::Disabled, Permission::ReadOnly),
@@ -218,7 +292,7 @@
};
let s = r#"
- id: "1234"
+ name: "1234"
description: "Description of the flag"
value {
state: DISABLED
@@ -238,7 +312,7 @@
#[test]
fn test_flag_try_from_text_proto_bad_input() {
let s = r#"
- id: "a"
+ name: "a"
description: "Description of the flag"
"#;
let error = Flag::try_from_text_proto(s).unwrap_err();
@@ -255,7 +329,7 @@
assert!(format!("{:?}", error).contains("Message not initialized"));
let s = r#"
- id: "a"
+ name: "a"
description: "Description of the flag"
value {
state: ENABLED
@@ -271,23 +345,27 @@
}
#[test]
- fn test_flag_try_from_text_proto_list() {
- let expected = vec![
- Flag {
- id: "a".to_owned(),
- description: "A".to_owned(),
- values: vec![Value::default(FlagState::Enabled, Permission::ReadOnly)],
- },
- Flag {
- id: "b".to_owned(),
- description: "B".to_owned(),
- values: vec![Value::default(FlagState::Disabled, Permission::ReadWrite)],
- },
- ];
+ fn test_namespace_try_from_text_proto() {
+ let expected = Namespace {
+ namespace: "ns".to_owned(),
+ flags: vec![
+ Flag {
+ name: "a".to_owned(),
+ description: "A".to_owned(),
+ values: vec![Value::default(FlagState::Enabled, Permission::ReadOnly)],
+ },
+ Flag {
+ name: "b".to_owned(),
+ description: "B".to_owned(),
+ values: vec![Value::default(FlagState::Disabled, Permission::ReadWrite)],
+ },
+ ],
+ };
let s = r#"
+ namespace: "ns"
flag {
- id: "a"
+ name: "a"
description: "A"
value {
state: ENABLED
@@ -295,7 +373,7 @@
}
}
flag {
- id: "b"
+ name: "b"
description: "B"
value {
state: DISABLED
@@ -303,7 +381,7 @@
}
}
"#;
- let actual = Flag::try_from_text_proto_list(s).unwrap();
+ let actual = Namespace::try_from_text_proto(s).unwrap();
assert_eq!(expected, actual);
}
@@ -311,13 +389,15 @@
#[test]
fn test_override_try_from_text_proto_list() {
let expected = Override {
- id: "1234".to_owned(),
+ namespace: "ns".to_owned(),
+ name: "1234".to_owned(),
state: FlagState::Enabled,
permission: Permission::ReadOnly,
};
let s = r#"
- id: "1234"
+ namespace: "ns"
+ name: "1234"
state: ENABLED
permission: READ_ONLY
"#;
@@ -329,7 +409,7 @@
#[test]
fn test_flag_resolve() {
let flag = Flag {
- id: "a".to_owned(),
+ name: "a".to_owned(),
description: "A".to_owned(),
values: vec![
Value::default(FlagState::Disabled, Permission::ReadOnly),
diff --git a/tools/aconfig/src/cache.rs b/tools/aconfig/src/cache.rs
index 94443d7..4b46c42 100644
--- a/tools/aconfig/src/cache.rs
+++ b/tools/aconfig/src/cache.rs
@@ -22,7 +22,7 @@
use crate::commands::Source;
#[derive(Serialize, Deserialize, Debug)]
-pub struct TracePoint {
+pub struct Tracepoint {
pub source: Source,
pub state: FlagState,
pub permission: Permission,
@@ -30,22 +30,28 @@
#[derive(Serialize, Deserialize, Debug)]
pub struct Item {
- pub id: String,
+ // TODO: duplicating the Cache.namespace as Item.namespace makes the internal representation
+ // closer to the proto message `parsed_flag`; hopefully this will enable us to replace the Item
+ // struct and use a newtype instead once aconfig has matured. Until then, namespace should
+ // really be a Cow<String>.
+ pub namespace: String,
+ pub name: String,
pub description: String,
pub state: FlagState,
pub permission: Permission,
- pub trace: Vec<TracePoint>,
+ pub trace: Vec<Tracepoint>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Cache {
build_id: u32,
+ namespace: String,
items: Vec<Item>,
}
impl Cache {
- pub fn new(build_id: u32) -> Cache {
- Cache { build_id, items: vec![] }
+ pub fn new(build_id: u32, namespace: String) -> Cache {
+ Cache { build_id, namespace, items: vec![] }
}
pub fn read_from_reader(reader: impl Read) -> Result<Cache> {
@@ -57,31 +63,36 @@
}
pub fn add_flag(&mut self, source: Source, flag: Flag) -> Result<()> {
- if self.items.iter().any(|item| item.id == flag.id) {
+ if self.items.iter().any(|item| item.name == flag.name) {
return Err(anyhow!(
"failed to add flag {} from {}: flag already defined",
- flag.id,
+ flag.name,
source,
));
}
let (state, permission) = flag.resolve(self.build_id);
self.items.push(Item {
- id: flag.id.clone(),
+ namespace: self.namespace.clone(),
+ name: flag.name.clone(),
description: flag.description,
state,
permission,
- trace: vec![TracePoint { source, state, permission }],
+ trace: vec![Tracepoint { source, state, permission }],
});
Ok(())
}
pub fn add_override(&mut self, source: Source, override_: Override) -> Result<()> {
- let Some(existing_item) = self.items.iter_mut().find(|item| item.id == override_.id) else {
- return Err(anyhow!("failed to override flag {}: unknown flag", override_.id));
+ if override_.namespace != self.namespace {
+ // TODO: print warning?
+ return Ok(());
+ }
+ let Some(existing_item) = self.items.iter_mut().find(|item| item.name == override_.name) else {
+ return Err(anyhow!("failed to override flag {}: unknown flag", override_.name));
};
existing_item.state = override_.state;
existing_item.permission = override_.permission;
- existing_item.trace.push(TracePoint {
+ existing_item.trace.push(Tracepoint {
source,
state: override_.state,
permission: override_.permission,
@@ -92,9 +103,11 @@
pub fn iter(&self) -> impl Iterator<Item = &Item> {
self.items.iter()
}
-}
-impl Item {}
+ pub fn into_iter(self) -> impl Iterator<Item = Item> {
+ self.items.into_iter()
+ }
+}
#[cfg(test)]
mod tests {
@@ -103,12 +116,12 @@
#[test]
fn test_add_flag() {
- let mut cache = Cache::new(1);
+ let mut cache = Cache::new(1, "ns".to_string());
cache
.add_flag(
Source::File("first.txt".to_string()),
Flag {
- id: "foo".to_string(),
+ name: "foo".to_string(),
description: "desc".to_string(),
values: vec![Value::default(FlagState::Enabled, Permission::ReadOnly)],
},
@@ -118,7 +131,7 @@
.add_flag(
Source::File("second.txt".to_string()),
Flag {
- id: "foo".to_string(),
+ name: "foo".to_string(),
description: "desc".to_string(),
values: vec![Value::default(FlagState::Disabled, Permission::ReadOnly)],
},
@@ -132,17 +145,18 @@
#[test]
fn test_add_override() {
- fn check(cache: &Cache, id: &str, expected: (FlagState, Permission)) -> bool {
- let item = cache.iter().find(|&item| item.id == id).unwrap();
+ fn check(cache: &Cache, name: &str, expected: (FlagState, Permission)) -> bool {
+ let item = cache.iter().find(|&item| item.name == name).unwrap();
item.state == expected.0 && item.permission == expected.1
}
- let mut cache = Cache::new(1);
+ let mut cache = Cache::new(1, "ns".to_string());
let error = cache
.add_override(
Source::Memory,
Override {
- id: "foo".to_string(),
+ namespace: "ns".to_string(),
+ name: "foo".to_string(),
state: FlagState::Enabled,
permission: Permission::ReadOnly,
},
@@ -154,7 +168,7 @@
.add_flag(
Source::File("first.txt".to_string()),
Flag {
- id: "foo".to_string(),
+ name: "foo".to_string(),
description: "desc".to_string(),
values: vec![Value::default(FlagState::Enabled, Permission::ReadOnly)],
},
@@ -167,7 +181,8 @@
.add_override(
Source::Memory,
Override {
- id: "foo".to_string(),
+ namespace: "ns".to_string(),
+ name: "foo".to_string(),
state: FlagState::Disabled,
permission: Permission::ReadWrite,
},
@@ -180,12 +195,27 @@
.add_override(
Source::Memory,
Override {
- id: "foo".to_string(),
+ namespace: "ns".to_string(),
+ name: "foo".to_string(),
state: FlagState::Enabled,
permission: Permission::ReadWrite,
},
)
.unwrap();
assert!(check(&cache, "foo", (FlagState::Enabled, Permission::ReadWrite)));
+
+ // different namespace -> no-op
+ cache
+ .add_override(
+ Source::Memory,
+ Override {
+ namespace: "some-other-namespace".to_string(),
+ name: "foo".to_string(),
+ state: FlagState::Enabled,
+ permission: Permission::ReadOnly,
+ },
+ )
+ .unwrap();
+ assert!(check(&cache, "foo", (FlagState::Enabled, Permission::ReadWrite)));
}
}
diff --git a/tools/aconfig/src/commands.rs b/tools/aconfig/src/commands.rs
index 73d3357..2c80a4a 100644
--- a/tools/aconfig/src/commands.rs
+++ b/tools/aconfig/src/commands.rs
@@ -14,14 +14,16 @@
* limitations under the License.
*/
-use anyhow::{Context, Result};
+use anyhow::{ensure, Context, Result};
use clap::ValueEnum;
+use protobuf::Message;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::io::Read;
-use crate::aconfig::{Flag, Override};
+use crate::aconfig::{Namespace, Override};
use crate::cache::Cache;
+use crate::protos::ProtoParsedFlags;
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum Source {
@@ -44,15 +46,27 @@
pub reader: Box<dyn Read>,
}
-pub fn create_cache(build_id: u32, aconfigs: Vec<Input>, overrides: Vec<Input>) -> Result<Cache> {
- let mut cache = Cache::new(build_id);
+pub fn create_cache(
+ build_id: u32,
+ namespace: &str,
+ aconfigs: Vec<Input>,
+ overrides: Vec<Input>,
+) -> Result<Cache> {
+ let mut cache = Cache::new(build_id, namespace.to_owned());
for mut input in aconfigs {
let mut contents = String::new();
input.reader.read_to_string(&mut contents)?;
- let flags = Flag::try_from_text_proto_list(&contents)
+ let ns = Namespace::try_from_text_proto(&contents)
.with_context(|| format!("Failed to parse {}", input.source))?;
- for flag in flags {
+ ensure!(
+ namespace == ns.namespace,
+ "Failed to parse {}: expected namespace {}, got {}",
+ input.source,
+ namespace,
+ ns.namespace
+ );
+ for flag in ns.flags.into_iter() {
cache.add_flag(input.source.clone(), flag)?;
}
}
@@ -74,22 +88,32 @@
pub enum Format {
Text,
Debug,
+ Protobuf,
}
-pub fn dump_cache(cache: Cache, format: Format) -> Result<()> {
+pub fn dump_cache(cache: Cache, format: Format) -> Result<Vec<u8>> {
match format {
Format::Text => {
+ let mut lines = vec![];
for item in cache.iter() {
- println!("{}: {:?}", item.id, item.state);
+ lines.push(format!("{}: {:?}\n", item.name, item.state));
}
+ Ok(lines.concat().into())
}
Format::Debug => {
+ let mut lines = vec![];
for item in cache.iter() {
- println!("{:?}", item);
+ lines.push(format!("{:?}\n", item));
}
+ Ok(lines.concat().into())
+ }
+ Format::Protobuf => {
+ let parsed_flags: ProtoParsedFlags = cache.into();
+ let mut output = vec![];
+ parsed_flags.write_to_vec(&mut output)?;
+ Ok(output)
}
}
- Ok(())
}
#[cfg(test)]
@@ -97,30 +121,80 @@
use super::*;
use crate::aconfig::{FlagState, Permission};
- #[test]
- fn test_create_cache() {
+ fn create_test_cache() -> Cache {
let s = r#"
+ namespace: "ns"
flag {
- id: "a"
+ name: "a"
description: "Description of a"
value {
state: ENABLED
permission: READ_WRITE
}
}
+ flag {
+ name: "b"
+ description: "Description of b"
+ value {
+ state: ENABLED
+ permission: READ_ONLY
+ }
+ }
"#;
let aconfigs = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }];
let o = r#"
- override {
- id: "a"
+ flag_override {
+ namespace: "ns"
+ name: "a"
state: DISABLED
permission: READ_ONLY
}
"#;
let overrides = vec![Input { source: Source::Memory, reader: Box::new(o.as_bytes()) }];
- let cache = create_cache(1, aconfigs, overrides).unwrap();
- let item = cache.iter().find(|&item| item.id == "a").unwrap();
+ create_cache(1, "ns", aconfigs, overrides).unwrap()
+ }
+
+ #[test]
+ fn test_create_cache() {
+ let cache = create_test_cache(); // calls create_cache
+ let item = cache.iter().find(|&item| item.name == "a").unwrap();
assert_eq!(FlagState::Disabled, item.state);
assert_eq!(Permission::ReadOnly, item.permission);
}
+
+ #[test]
+ fn test_dump_text_format() {
+ let cache = create_test_cache();
+ let bytes = dump_cache(cache, Format::Text).unwrap();
+ let text = std::str::from_utf8(&bytes).unwrap();
+ assert!(text.contains("a: Disabled"));
+ }
+
+ #[test]
+ fn test_dump_protobuf_format() {
+ use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoTracepoint};
+ use protobuf::Message;
+
+ let cache = create_test_cache();
+ let bytes = dump_cache(cache, Format::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.namespace(), "ns");
+ assert_eq!(item.name(), "b");
+ assert_eq!(item.description(), "Description of b");
+ assert_eq!(item.state(), ProtoFlagState::ENABLED);
+ assert_eq!(item.permission(), ProtoFlagPermission::READ_ONLY);
+ let mut tp = ProtoTracepoint::new();
+ tp.set_source("<memory>".to_string());
+ tp.set_state(ProtoFlagState::ENABLED);
+ tp.set_permission(ProtoFlagPermission::READ_ONLY);
+ assert_eq!(item.trace, vec![tp]);
+ }
}
diff --git a/tools/aconfig/src/main.rs b/tools/aconfig/src/main.rs
index 62750ae..f253735 100644
--- a/tools/aconfig/src/main.rs
+++ b/tools/aconfig/src/main.rs
@@ -19,6 +19,8 @@
use anyhow::Result;
use clap::{builder::ArgAction, builder::EnumValueParser, Arg, ArgMatches, Command};
use std::fs;
+use std::io;
+use std::io::Write;
mod aconfig;
mod cache;
@@ -39,17 +41,21 @@
.value_parser(clap::value_parser!(u32))
.required(true),
)
+ .arg(Arg::new("namespace").long("namespace").required(true))
.arg(Arg::new("aconfig").long("aconfig").action(ArgAction::Append))
.arg(Arg::new("override").long("override").action(ArgAction::Append))
.arg(Arg::new("cache").long("cache").required(true)),
)
.subcommand(
- Command::new("dump").arg(Arg::new("cache").long("cache").required(true)).arg(
- Arg::new("format")
- .long("format")
- .value_parser(EnumValueParser::<commands::Format>::new())
- .default_value("text"),
- ),
+ Command::new("dump")
+ .arg(Arg::new("cache").long("cache").required(true))
+ .arg(
+ Arg::new("format")
+ .long("format")
+ .value_parser(EnumValueParser::<commands::Format>::new())
+ .default_value("text"),
+ )
+ .arg(Arg::new("out").long("out").default_value("-")),
)
}
@@ -67,9 +73,10 @@
match matches.subcommand() {
Some(("create-cache", sub_matches)) => {
let build_id = *sub_matches.get_one::<u32>("build-id").unwrap();
+ let namespace = sub_matches.get_one::<String>("namespace").unwrap();
let aconfigs = open_zero_or_more_files(sub_matches, "aconfig")?;
let overrides = open_zero_or_more_files(sub_matches, "override")?;
- let cache = commands::create_cache(build_id, aconfigs, overrides)?;
+ let cache = commands::create_cache(build_id, namespace, aconfigs, overrides)?;
let path = sub_matches.get_one::<String>("cache").unwrap();
let file = fs::File::create(path)?;
cache.write_to_writer(file)?;
@@ -79,7 +86,14 @@
let file = fs::File::open(path)?;
let cache = Cache::read_from_reader(file)?;
let format = sub_matches.get_one("format").unwrap();
- commands::dump_cache(cache, *format)?;
+ let output = commands::dump_cache(cache, *format)?;
+ let path = sub_matches.get_one::<String>("out").unwrap();
+ let mut file: Box<dyn Write> = if path == "-" {
+ Box::new(io::stdout())
+ } else {
+ Box::new(fs::File::create(path)?)
+ };
+ file.write_all(&output)?;
}
_ => unreachable!(),
}
diff --git a/tools/aconfig/src/protos.rs b/tools/aconfig/src/protos.rs
index 604eca4..5965a09 100644
--- a/tools/aconfig/src/protos.rs
+++ b/tools/aconfig/src/protos.rs
@@ -28,51 +28,69 @@
// ---- When building with the Android tool-chain ----
#[cfg(not(feature = "cargo"))]
-pub use aconfig_protos::aconfig::Android_config as ProtoAndroidConfig;
+pub use aconfig_protos::aconfig::Namespace as ProtoNamespace;
#[cfg(not(feature = "cargo"))]
-pub use aconfig_protos::aconfig::Value as ProtoValue;
+pub use aconfig_protos::aconfig::Flag_value as ProtoFlagDefinitionValue;
#[cfg(not(feature = "cargo"))]
-pub use aconfig_protos::aconfig::Flag as ProtoFlag;
+pub use aconfig_protos::aconfig::Flag_definition as ProtoFlagDefinition;
#[cfg(not(feature = "cargo"))]
-pub use aconfig_protos::aconfig::Override_config as ProtoOverrideConfig;
+pub use aconfig_protos::aconfig::Flag_overrides as ProtoFlagOverrides;
#[cfg(not(feature = "cargo"))]
-pub use aconfig_protos::aconfig::Override as ProtoOverride;
+pub use aconfig_protos::aconfig::Flag_override as ProtoFlagOverride;
#[cfg(not(feature = "cargo"))]
-pub use aconfig_protos::aconfig::Permission as ProtoPermission;
+pub use aconfig_protos::aconfig::Flag_permission as ProtoFlagPermission;
#[cfg(not(feature = "cargo"))]
pub use aconfig_protos::aconfig::Flag_state as ProtoFlagState;
+#[cfg(not(feature = "cargo"))]
+pub use aconfig_protos::aconfig::Parsed_flags as ProtoParsedFlags;
+
+#[cfg(not(feature = "cargo"))]
+pub use aconfig_protos::aconfig::Parsed_flag as ProtoParsedFlag;
+
+#[cfg(not(feature = "cargo"))]
+pub use aconfig_protos::aconfig::Tracepoint as ProtoTracepoint;
+
// ---- When building with cargo ----
#[cfg(feature = "cargo")]
include!(concat!(env!("OUT_DIR"), "/aconfig_proto/mod.rs"));
#[cfg(feature = "cargo")]
-pub use aconfig::Android_config as ProtoAndroidConfig;
+pub use aconfig::Namespace as ProtoNamespace;
#[cfg(feature = "cargo")]
-pub use aconfig::Value as ProtoValue;
+pub use aconfig::Flag_value as ProtoFlagDefinitionValue;
#[cfg(feature = "cargo")]
-pub use aconfig::Flag as ProtoFlag;
+pub use aconfig::Flag_definition as ProtoFlagDefinition;
#[cfg(feature = "cargo")]
-pub use aconfig::Override_config as ProtoOverrideConfig;
+pub use aconfig::Flag_overrides as ProtoFlagOverrides;
#[cfg(feature = "cargo")]
-pub use aconfig::Override as ProtoOverride;
+pub use aconfig::Flag_override as ProtoFlagOverride;
#[cfg(feature = "cargo")]
-pub use aconfig::Permission as ProtoPermission;
+pub use aconfig::Flag_permission as ProtoFlagPermission;
#[cfg(feature = "cargo")]
pub use aconfig::Flag_state as ProtoFlagState;
+#[cfg(feature = "cargo")]
+pub use aconfig::Parsed_flags as ProtoParsedFlags;
+
+#[cfg(feature = "cargo")]
+pub use aconfig::Parsed_flag as ProtoParsedFlag;
+
+#[cfg(feature = "cargo")]
+pub use aconfig::Tracepoint as ProtoTracepoint;
+
// ---- Common for both the Android tool-chain and cargo ----
use anyhow::Result;