Loading tools/aconfig/aflags/src/device_config_source.rs +50 −16 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ fn convert_parsed_flag(flag: &ProtoParsedFlag) -> Flag { name, container, value, staged_value: None, permission, value_picked_from: ValuePickedFrom::Default, } Loading Loading @@ -123,24 +124,56 @@ fn read_device_config_flags() -> Result<HashMap<String, FlagValue>> { parse_device_config(&list_output) } fn reconcile(pb_flags: &[Flag], dc_flags: HashMap<String, FlagValue>) -> Vec<Flag> { /// Parse the list of newline-separated staged flags. /// /// The output is a newline-sepaarated list of entries which follow this format: /// `namespace*flagname=value` /// /// The resulting map maps from `namespace/flagname` to `value`, if a staged flag exists for /// `namespace/flagname`. fn parse_staged_flags(raw: &str) -> Result<HashMap<String, FlagValue>> { let mut flags = HashMap::new(); for line in raw.split('\n') { match (line.find('*'), line.find('=')) { (Some(star_index), Some(equal_index)) => { let namespace = &line[..star_index]; let flag = &line[star_index + 1..equal_index]; if let Ok(value) = FlagValue::try_from(&line[equal_index + 1..]) { flags.insert(namespace.to_owned() + "/" + flag, value); } } _ => continue, }; } Ok(flags) } fn read_staged_flags() -> Result<HashMap<String, FlagValue>> { let staged_flags_output = read_device_config_output(&["list", "staged"])?; parse_staged_flags(&staged_flags_output) } fn reconcile( pb_flags: &[Flag], dc_flags: HashMap<String, FlagValue>, staged_flags: HashMap<String, FlagValue>, ) -> Vec<Flag> { pb_flags .iter() .map(|f| { dc_flags .get(&format!("{}/{}.{}", f.namespace, f.package, f.name)) .map(|value| { if *value == f.value { Flag { value_picked_from: ValuePickedFrom::Default, ..f.clone() } } else { Flag { value_picked_from: ValuePickedFrom::Server, value: *value, ..f.clone() } } let server_override = dc_flags.get(&format!("{}/{}", f.namespace, f.qualified_name())); let (value_picked_from, selected_value) = match server_override { Some(value) if *value != f.value => (ValuePickedFrom::Server, *value), _ => (ValuePickedFrom::Default, f.value), }; Flag { value_picked_from, value: selected_value, ..f.clone() } }) .unwrap_or(f.clone()) .map(|f| { let staged_value = staged_flags .get(&format!("{}/{}", f.namespace, f.qualified_name())) .map(|value| if *value != f.value { Some(*value) } else { None }) .unwrap_or(None); Flag { staged_value, ..f } }) .collect() } Loading @@ -149,8 +182,9 @@ impl FlagSource for DeviceConfigSource { fn list_flags() -> Result<Vec<Flag>> { let pb_flags = read_pb_files()?; let dc_flags = read_device_config_flags()?; let staged_flags = read_staged_flags()?; let flags = reconcile(&pb_flags, dc_flags); let flags = reconcile(&pb_flags, dc_flags, staged_flags); Ok(flags) } Loading tools/aconfig/aflags/src/main.rs +29 −6 Original line number Diff line number Diff line Loading @@ -22,7 +22,7 @@ use clap::Parser; mod device_config_source; use device_config_source::DeviceConfigSource; #[derive(Clone, PartialEq)] #[derive(Clone, PartialEq, Debug)] enum FlagPermission { ReadOnly, ReadWrite, Loading @@ -37,7 +37,7 @@ impl ToString for FlagPermission { } } #[derive(Clone)] #[derive(Clone, Debug)] enum ValuePickedFrom { Default, Server, Loading Loading @@ -79,13 +79,14 @@ impl ToString for FlagValue { } } #[derive(Clone)] #[derive(Clone, Debug)] struct Flag { namespace: String, name: String, package: String, container: String, value: FlagValue, staged_value: Option<FlagValue>, permission: FlagPermission, value_picked_from: ValuePickedFrom, } Loading @@ -94,6 +95,13 @@ impl Flag { fn qualified_name(&self) -> String { format!("{}.{}", self.package, self.name) } fn display_staged_value(&self) -> String { match self.staged_value { Some(v) => format!("(->{})", v.to_string()), None => "-".to_string(), } } } trait FlagSource { Loading @@ -110,6 +118,10 @@ Rows in the table from the `list` command follow this format: * `package`: package set for this flag in its .aconfig definition. * `flag_name`: flag name, also set in definition. * `value`: the value read from the flag. * `staged_value`: the value on next boot: + `-`: same as current value + `(->enabled) flipped to enabled on boot. + `(->disabled) flipped to disabled on boot. * `provenance`: one of: + `default`: the flag value comes from its build-time default. + `server`: the flag value comes from a server override. Loading Loading @@ -145,6 +157,7 @@ enum Command { struct PaddingInfo { longest_flag_col: usize, longest_val_col: usize, longest_staged_val_col: usize, longest_value_picked_from_col: usize, longest_permission_col: usize, } Loading @@ -156,15 +169,20 @@ fn format_flag_row(flag: &Flag, info: &PaddingInfo) -> String { let val = flag.value.to_string(); let p1 = info.longest_val_col + 1; let staged_val = flag.display_staged_value(); let p2 = info.longest_staged_val_col + 1; let value_picked_from = flag.value_picked_from.to_string(); let p2 = info.longest_value_picked_from_col + 1; let p3 = info.longest_value_picked_from_col + 1; let perm = flag.permission.to_string(); let p3 = info.longest_permission_col + 1; let p4 = info.longest_permission_col + 1; let container = &flag.container; format!("{full_name:p0$}{val:p1$}{value_picked_from:p2$}{perm:p3$}{container}\n") format!( "{full_name:p0$}{val:p1$}{staged_val:p2$}{value_picked_from:p3$}{perm:p4$}{container}\n" ) } fn set_flag(qualified_name: &str, value: &str) -> Result<()> { Loading @@ -188,6 +206,11 @@ fn list() -> Result<String> { let padding_info = PaddingInfo { longest_flag_col: flags.iter().map(|f| f.qualified_name().len()).max().unwrap_or(0), longest_val_col: flags.iter().map(|f| f.value.to_string().len()).max().unwrap_or(0), longest_staged_val_col: flags .iter() .map(|f| f.display_staged_value().len()) .max() .unwrap_or(0), longest_value_picked_from_col: flags .iter() .map(|f| f.value_picked_from.to_string().len()) Loading Loading
tools/aconfig/aflags/src/device_config_source.rs +50 −16 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ fn convert_parsed_flag(flag: &ProtoParsedFlag) -> Flag { name, container, value, staged_value: None, permission, value_picked_from: ValuePickedFrom::Default, } Loading Loading @@ -123,24 +124,56 @@ fn read_device_config_flags() -> Result<HashMap<String, FlagValue>> { parse_device_config(&list_output) } fn reconcile(pb_flags: &[Flag], dc_flags: HashMap<String, FlagValue>) -> Vec<Flag> { /// Parse the list of newline-separated staged flags. /// /// The output is a newline-sepaarated list of entries which follow this format: /// `namespace*flagname=value` /// /// The resulting map maps from `namespace/flagname` to `value`, if a staged flag exists for /// `namespace/flagname`. fn parse_staged_flags(raw: &str) -> Result<HashMap<String, FlagValue>> { let mut flags = HashMap::new(); for line in raw.split('\n') { match (line.find('*'), line.find('=')) { (Some(star_index), Some(equal_index)) => { let namespace = &line[..star_index]; let flag = &line[star_index + 1..equal_index]; if let Ok(value) = FlagValue::try_from(&line[equal_index + 1..]) { flags.insert(namespace.to_owned() + "/" + flag, value); } } _ => continue, }; } Ok(flags) } fn read_staged_flags() -> Result<HashMap<String, FlagValue>> { let staged_flags_output = read_device_config_output(&["list", "staged"])?; parse_staged_flags(&staged_flags_output) } fn reconcile( pb_flags: &[Flag], dc_flags: HashMap<String, FlagValue>, staged_flags: HashMap<String, FlagValue>, ) -> Vec<Flag> { pb_flags .iter() .map(|f| { dc_flags .get(&format!("{}/{}.{}", f.namespace, f.package, f.name)) .map(|value| { if *value == f.value { Flag { value_picked_from: ValuePickedFrom::Default, ..f.clone() } } else { Flag { value_picked_from: ValuePickedFrom::Server, value: *value, ..f.clone() } } let server_override = dc_flags.get(&format!("{}/{}", f.namespace, f.qualified_name())); let (value_picked_from, selected_value) = match server_override { Some(value) if *value != f.value => (ValuePickedFrom::Server, *value), _ => (ValuePickedFrom::Default, f.value), }; Flag { value_picked_from, value: selected_value, ..f.clone() } }) .unwrap_or(f.clone()) .map(|f| { let staged_value = staged_flags .get(&format!("{}/{}", f.namespace, f.qualified_name())) .map(|value| if *value != f.value { Some(*value) } else { None }) .unwrap_or(None); Flag { staged_value, ..f } }) .collect() } Loading @@ -149,8 +182,9 @@ impl FlagSource for DeviceConfigSource { fn list_flags() -> Result<Vec<Flag>> { let pb_flags = read_pb_files()?; let dc_flags = read_device_config_flags()?; let staged_flags = read_staged_flags()?; let flags = reconcile(&pb_flags, dc_flags); let flags = reconcile(&pb_flags, dc_flags, staged_flags); Ok(flags) } Loading
tools/aconfig/aflags/src/main.rs +29 −6 Original line number Diff line number Diff line Loading @@ -22,7 +22,7 @@ use clap::Parser; mod device_config_source; use device_config_source::DeviceConfigSource; #[derive(Clone, PartialEq)] #[derive(Clone, PartialEq, Debug)] enum FlagPermission { ReadOnly, ReadWrite, Loading @@ -37,7 +37,7 @@ impl ToString for FlagPermission { } } #[derive(Clone)] #[derive(Clone, Debug)] enum ValuePickedFrom { Default, Server, Loading Loading @@ -79,13 +79,14 @@ impl ToString for FlagValue { } } #[derive(Clone)] #[derive(Clone, Debug)] struct Flag { namespace: String, name: String, package: String, container: String, value: FlagValue, staged_value: Option<FlagValue>, permission: FlagPermission, value_picked_from: ValuePickedFrom, } Loading @@ -94,6 +95,13 @@ impl Flag { fn qualified_name(&self) -> String { format!("{}.{}", self.package, self.name) } fn display_staged_value(&self) -> String { match self.staged_value { Some(v) => format!("(->{})", v.to_string()), None => "-".to_string(), } } } trait FlagSource { Loading @@ -110,6 +118,10 @@ Rows in the table from the `list` command follow this format: * `package`: package set for this flag in its .aconfig definition. * `flag_name`: flag name, also set in definition. * `value`: the value read from the flag. * `staged_value`: the value on next boot: + `-`: same as current value + `(->enabled) flipped to enabled on boot. + `(->disabled) flipped to disabled on boot. * `provenance`: one of: + `default`: the flag value comes from its build-time default. + `server`: the flag value comes from a server override. Loading Loading @@ -145,6 +157,7 @@ enum Command { struct PaddingInfo { longest_flag_col: usize, longest_val_col: usize, longest_staged_val_col: usize, longest_value_picked_from_col: usize, longest_permission_col: usize, } Loading @@ -156,15 +169,20 @@ fn format_flag_row(flag: &Flag, info: &PaddingInfo) -> String { let val = flag.value.to_string(); let p1 = info.longest_val_col + 1; let staged_val = flag.display_staged_value(); let p2 = info.longest_staged_val_col + 1; let value_picked_from = flag.value_picked_from.to_string(); let p2 = info.longest_value_picked_from_col + 1; let p3 = info.longest_value_picked_from_col + 1; let perm = flag.permission.to_string(); let p3 = info.longest_permission_col + 1; let p4 = info.longest_permission_col + 1; let container = &flag.container; format!("{full_name:p0$}{val:p1$}{value_picked_from:p2$}{perm:p3$}{container}\n") format!( "{full_name:p0$}{val:p1$}{staged_val:p2$}{value_picked_from:p3$}{perm:p4$}{container}\n" ) } fn set_flag(qualified_name: &str, value: &str) -> Result<()> { Loading @@ -188,6 +206,11 @@ fn list() -> Result<String> { let padding_info = PaddingInfo { longest_flag_col: flags.iter().map(|f| f.qualified_name().len()).max().unwrap_or(0), longest_val_col: flags.iter().map(|f| f.value.to_string().len()).max().unwrap_or(0), longest_staged_val_col: flags .iter() .map(|f| f.display_staged_value().len()) .max() .unwrap_or(0), longest_value_picked_from_col: flags .iter() .map(|f| f.value_picked_from.to_string().len()) Loading