feat: print-config subset of config (#3179)

* feat: print-config subset of config

* only print format helpers with format config

* improve help text

* also change argument name
This commit is contained in:
Matthew Donoughe 2021-11-15 00:45:19 -05:00 committed by GitHub
parent 9d443dff9f
commit c3e33ea1c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 145 additions and 18 deletions

View File

@ -73,7 +73,7 @@ fn handle_update_configuration(doc: &mut Document, name: &str, value: &str) -> R
Ok(()) Ok(())
} }
pub fn print_configuration(use_default: bool) { pub fn print_configuration(use_default: bool, paths: &[&str]) {
let config = if use_default { let config = if use_default {
// Get default config // Get default config
let default_config = crate::configs::FullConfig::default(); let default_config = crate::configs::FullConfig::default();
@ -88,31 +88,100 @@ pub fn print_configuration(use_default: bool) {
toml::value::Value::try_from(user_config).unwrap() toml::value::Value::try_from(user_config).unwrap()
}; };
let string_config = toml::to_string_pretty(&config).unwrap();
println!("# Warning: This config does not include keys that have an unset value\n"); println!("# Warning: This config does not include keys that have an unset value\n");
println!(
"# $all is shorthand for {}",
PROMPT_ORDER
.iter()
.map(|module_name| format!("${}", module_name))
.collect::<String>()
);
// Unwrapping is fine because config is based on FullConfig // These are only used for format specifiers so don't print them if we aren't showing formats.
let custom_modules = config.get("custom").unwrap().as_table().unwrap(); if paths.is_empty()
if !use_default && !custom_modules.is_empty() { || paths
.iter()
.any(|&path| path == "format" || path == "right_format")
{
println!( println!(
"# $custom (excluding any modules already listed in `format`) is shorthand for {}", "# $all is shorthand for {}",
custom_modules PROMPT_ORDER
.keys() .iter()
.map(|module_name| format!("${{custom.{}}}", module_name)) .map(|module_name| format!("${}", module_name))
.collect::<String>() .collect::<String>()
); );
// Unwrapping is fine because config is based on FullConfig
let custom_modules = config.get("custom").unwrap().as_table().unwrap();
if !use_default && !custom_modules.is_empty() {
println!(
"# $custom (excluding any modules already listed in `format`) is shorthand for {}",
custom_modules
.keys()
.map(|module_name| format!("${{custom.{}}}", module_name))
.collect::<String>()
);
}
} }
let print_config = if paths.is_empty() {
config
} else {
extract_toml_paths(config, paths)
};
let string_config = toml::to_string_pretty(&print_config).unwrap();
println!("{}", string_config); println!("{}", string_config);
} }
fn extract_toml_paths(mut config: toml::Value, paths: &[&str]) -> toml::Value {
// Extract all the requested sections into a new configuration.
let mut subset = toml::value::Table::new();
let config = if let Some(config) = config.as_table_mut() {
config
} else {
// This function doesn't make any sense if the root is not a table.
return toml::Value::Table(subset);
};
'paths: for &path in paths {
let path_segments: Vec<_> = path.split('.').collect();
let (&end, parents) = path_segments.split_last().unwrap_or((&"", &[]));
// Locate the parent table to remove the value from.
let mut source_cursor = &mut *config;
for &segment in parents {
source_cursor = if let Some(child) = source_cursor
.get_mut(segment)
.and_then(|value| value.as_table_mut())
{
child
} else {
// We didn't find a value for this path, so move on to the next path.
continue 'paths;
}
}
// Extract the value to move.
let value = if let Some(value) = source_cursor.remove(end) {
value
} else {
// We didn't find a value for this path, so move on to the next path.
continue 'paths;
};
// Create a destination for that value.
let mut destination_cursor = &mut subset;
for &segment in &path_segments[..path_segments.len() - 1] {
// Because we initialize `subset` to be a table, and only add additional values that
// exist in `config`, it's impossible for the value here to not be a table.
destination_cursor = destination_cursor
.entry(segment)
.or_insert_with(|| toml::Value::Table(toml::value::Table::new()))
.as_table_mut()
.unwrap();
}
destination_cursor.insert(end.to_owned(), value);
}
toml::Value::Table(subset)
}
pub fn toggle_configuration(name: &str, key: &str) { pub fn toggle_configuration(name: &str, key: &str) {
let mut doc = get_configuration_edit(); let mut doc = get_configuration_edit();
@ -306,6 +375,54 @@ mod tests {
assert_eq!(STD_EDITOR, actual); assert_eq!(STD_EDITOR, actual);
} }
#[test]
fn test_extract_toml_paths() {
let config = toml::toml! {
extract_root = true
ignore_root = false
[extract_section]
ok = true
[extract_section.subsection]
ok = true
[ignore_section]
ok = false
[extract_subsection]
ok = false
[extract_subsection.extracted]
ok = true
[extract_subsection.ignored]
ok = false
};
let expected_config = toml::toml! {
extract_root = true
[extract_section]
ok = true
[extract_section.subsection]
ok = true
[extract_subsection.extracted]
ok = true
};
let actual_config = extract_toml_paths(
config,
&[
"extract_root",
"extract_section",
"extract_subsection.extracted",
],
);
assert_eq!(expected_config, actual_config);
}
fn create_doc() -> Document { fn create_doc() -> Document {
let config = concat!( let config = concat!(
" # comment\n", " # comment\n",

View File

@ -166,6 +166,12 @@ fn main() {
.long("default") .long("default")
.help("Print the default instead of the computed config") .help("Print the default instead of the computed config")
.takes_value(false), .takes_value(false),
)
.arg(
Arg::with_name("name")
.help("Configuration keys to print")
.multiple(true)
.required(false),
), ),
) )
.subcommand( .subcommand(
@ -265,7 +271,11 @@ fn main() {
} }
("print-config", Some(sub_m)) => { ("print-config", Some(sub_m)) => {
let print_default = sub_m.is_present("default"); let print_default = sub_m.is_present("default");
configure::print_configuration(print_default) let paths = sub_m
.values_of("name")
.map(|paths| paths.collect::<Vec<&str>>())
.unwrap_or_default();
configure::print_configuration(print_default, &paths)
} }
("toggle", Some(sub_m)) => { ("toggle", Some(sub_m)) => {
if let Some(name) = sub_m.value_of("name") { if let Some(name) = sub_m.value_of("name") {