winbrew_cli\commands/
config.rs1use anyhow::Result;
7use std::io::Write;
8
9use crate::cli::ConfigCommand;
10use crate::commands::error::{reported, reported_with_hint};
11use crate::database::{Config, ConfigError, suggest_key};
12use crate::{CommandContext, app::config};
13use winbrew_ui::Ui;
14
15pub fn run(ctx: &CommandContext, config: &mut Config, command: ConfigCommand) -> Result<()> {
17 let mut ui = ctx.ui();
18 ui.page_title("Configuration");
19
20 match command {
21 ConfigCommand::List => list(config, &mut ui),
22 ConfigCommand::Get { key } => get(config, &mut ui, key.trim()),
23 ConfigCommand::Set { key, value } => set(config, &mut ui, key.trim(), value.as_deref()),
24 ConfigCommand::Unset { key } => unset(config, &mut ui, key.trim()),
25 }
26}
27
28fn list<W: Write>(config: &Config, ui: &mut Ui<W>) -> Result<()> {
30 let sections = config::list_sections(config)?;
31
32 if sections.is_empty() {
33 ui.notice("No configuration values are set.");
34 return Ok(());
35 }
36
37 for config::ConfigSection { title, entries } in §ions {
38 ui.notice(title);
39 ui.display_key_values(entries);
40 }
41
42 Ok(())
43}
44
45fn get<W: Write>(config: &Config, ui: &mut Ui<W>, key: &str) -> Result<()> {
47 let value = config::get_display_value(config, key)
48 .map_err(|err| config_key_error("retrieve", key, err))?;
49
50 ui.info(format_args!(
51 "{key} = {}{}",
52 value.value,
53 source_suffix(value.source)
54 ));
55 Ok(())
56}
57
58fn set<W: Write>(
63 config: &mut Config,
64 ui: &mut Ui<W>,
65 key: &str,
66 value: Option<&str>,
67) -> Result<()> {
68 let owned_prompt;
69 let clean_value = match value {
70 Some(value) => value.trim(),
71 None => {
72 owned_prompt = ui.prompt_text(&format!("Enter value for {key}"), None)?;
73 owned_prompt.trim()
74 }
75 };
76
77 if clean_value.is_empty() {
78 return Err(reported(
79 "Empty value is not allowed. Use 'unset' to remove a configuration key.",
80 ));
81 }
82
83 config::set_value(config, key, clean_value).map_err(|err| config_key_error("set", key, err))?;
84
85 ui.success(format!("{key} updated."));
86 Ok(())
87}
88
89fn unset<W: Write>(config: &mut Config, ui: &mut Ui<W>, key: &str) -> Result<()> {
91 config::unset_value(config, key).map_err(|err| config_key_error("remove", key, err))?;
92
93 ui.success(format!("{key} removed."));
94 Ok(())
95}
96
97fn source_suffix(source: config::ConfigValueSource) -> &'static str {
99 match source {
100 config::ConfigValueSource::Env => " (overridden by environment)",
101 config::ConfigValueSource::File => "",
102 }
103}
104
105fn config_key_error(action: &str, key: &str, err: anyhow::Error) -> anyhow::Error {
106 if let Some(ConfigError::UnknownKey { key: unknown_key }) = err.downcast_ref::<ConfigError>() {
107 let hint = match suggest_key(unknown_key) {
108 Some(suggested) => format!(
109 "Did you mean '{suggested}'? Use 'winbrew config list' to browse the available keys."
110 ),
111 None => "Use 'winbrew config list' to browse the available keys.".to_string(),
112 };
113
114 return reported_with_hint(format!("unknown config key '{unknown_key}'"), hint);
115 }
116
117 err.context(format!("Failed to {action} configuration for key: '{key}'"))
118}