use crate::settings::generated::SETTINGS_META;
use crate::{Result, settings::Settings};
use serde_json::json;
fn unknown_config_key_error(key: &str) -> eyre::Report {
let all_keys: Vec<&str> = SETTINGS_META
.keys()
.copied()
.chain(["jobs", "enabled_profiles", "disabled_profiles"])
.collect();
let msg = if let Some(suggestion) = xx::suggest::did_you_mean(key, &all_keys) {
format!("Unknown configuration key: '{}'. {}", key, suggestion)
} else {
format!("Unknown configuration key: '{}'", key)
};
eyre::eyre!("{}", msg)
}
#[derive(Debug, clap::Args)]
#[clap(visible_alias = "cfg")]
pub struct Config {
#[clap(subcommand)]
command: Option<ConfigCommand>,
}
#[derive(Debug, clap::Subcommand)]
enum ConfigCommand {
Dump(ConfigDump),
Explain(ConfigExplain),
Get(ConfigGet),
Sources(ConfigSources),
}
#[derive(Debug, clap::Args)]
struct ConfigDump {
#[clap(long, value_parser = ["json", "toml"], default_value = "json")]
format: String,
}
#[derive(Debug, clap::Args)]
struct ConfigGet {
key: String,
}
#[derive(Debug, clap::Args)]
struct ConfigExplain {
key: String,
}
#[derive(Debug, clap::Args)]
struct ConfigSources {}
impl Config {
pub async fn run(&self) -> Result<()> {
match &self.command {
Some(ConfigCommand::Dump(cmd)) => cmd.run(),
Some(ConfigCommand::Get(cmd)) => cmd.run(),
Some(ConfigCommand::Explain(cmd)) => cmd.run(),
Some(ConfigCommand::Sources(cmd)) => cmd.run(),
None => {
warn!("this output is almost certain to change in a future version");
let dump = ConfigDump {
format: "toml".to_string(),
};
dump.run()
}
}
}
}
impl ConfigDump {
fn run(&self) -> Result<()> {
let settings = Settings::try_get()?;
let mut map = serde_json::Map::new();
let full = serde_json::to_value(settings.as_ref())?;
for (key, _meta) in SETTINGS_META.iter() {
let k = (*key).to_string();
if k == "jobs" {
map.insert(k, json!(settings.jobs()));
continue;
}
if let Some(v) = full.get(key) {
map.insert(k, v.clone());
}
}
map.insert(
"enabled_profiles".to_string(),
json!(settings.enabled_profiles()),
);
map.insert(
"disabled_profiles".to_string(),
json!(settings.disabled_profiles()),
);
let output = serde_json::Value::Object(map);
match self.format.as_str() {
"json" => println!("{}", serde_json::to_string_pretty(&output)?),
"toml" => {
let toml_value: toml::Value = serde_json::from_value(output)?;
println!("{}", toml::to_string_pretty(&toml_value)?);
}
_ => unreachable!("Invalid format"),
}
Ok(())
}
}
impl ConfigGet {
fn run(&self) -> Result<()> {
let settings = Settings::try_get()?;
let value = if self.key == "jobs" {
json!(settings.jobs())
} else if self.key == "enabled_profiles" {
json!(settings.enabled_profiles())
} else if self.key == "disabled_profiles" {
json!(settings.disabled_profiles())
} else if SETTINGS_META.contains_key(self.key.as_str()) {
let full = serde_json::to_value(settings.as_ref())?;
full.get(&self.key).cloned().ok_or_else(|| {
eyre::eyre!("Key present in meta but missing in settings: {}", self.key)
})?
} else {
return Err(unknown_config_key_error(&self.key));
};
println!("{}", serde_json::to_string(&value)?);
Ok(())
}
}
impl ConfigExplain {
fn run(&self) -> Result<()> {
let settings = Settings::try_get()?;
let current_value = if self.key == "jobs" {
json!(settings.jobs())
} else if self.key == "enabled_profiles" {
json!(settings.enabled_profiles())
} else if self.key == "disabled_profiles" {
json!(settings.disabled_profiles())
} else if SETTINGS_META.contains_key(self.key.as_str()) {
let full = serde_json::to_value(settings.as_ref())?;
full.get(&self.key).cloned().ok_or_else(|| {
eyre::eyre!("Key present in meta but missing in settings: {}", self.key)
})?
} else {
return Err(unknown_config_key_error(&self.key));
};
let resolution_info = Settings::explain_value(&self.key)?;
println!("Configuration key: {}", self.key);
println!("Current value: {}", serde_json::to_string(¤t_value)?);
println!();
println!("{}", resolution_info);
Ok(())
}
}
impl ConfigSources {
fn run(&self) -> Result<()> {
println!("Configuration sources (in order of precedence):");
println!("1. CLI flags");
println!("2. Environment variables (HK_*)");
println!("3. Git config (local repo)");
println!("4. Git config (global/system)");
println!("5. User rc (.hkrc.pkl)");
println!("6. Project config (hk.pkl)");
println!("7. Built-in defaults");
println!();
println!("Note: Use 'hk config dump' to see current effective values");
Ok(())
}
}