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