diff --git a/crates/idx-cli/src/cli/config.rs b/crates/idx-cli/src/cli/config.rs index f6a3696..976c54a 100644 --- a/crates/idx-cli/src/cli/config.rs +++ b/crates/idx-cli/src/cli/config.rs @@ -1,7 +1,7 @@ use clap::{Args, Subcommand}; use crate::config::{config_path, ensure_default_config, get_config_value, set_config_value}; -use crate::output::{OutputFormat, json}; +use crate::output::{OutputSetting, json}; use idx_core::error::IdxError; #[derive(Debug, Args)] @@ -23,10 +23,11 @@ pub enum ConfigSubcommand { Path, } -pub fn handle(cmd: &ConfigCmd, format: &OutputFormat) -> Result<(), IdxError> { - // Each result as plain text, or one JSON document with -o json. +pub fn handle(cmd: &ConfigCmd, setting: OutputSetting) -> Result<(), IdxError> { + // Plain text, as scripts read `$(idx config path)`, unless JSON is asked + // for (-o json, IDX_OUTPUT, or general.output); auto stays text when piped. let print = |text: String, document: serde_json::Value| { - if format.is_json() { + if matches!(setting, OutputSetting::Json | OutputSetting::Jsonl) { json::print_json(&document) } else { println!("{}", crate::output::text::terminal_safe(&text)); diff --git a/crates/idx-cli/src/config.rs b/crates/idx-cli/src/config.rs index ba85b4c..a0ad8f3 100644 --- a/crates/idx-cli/src/config.rs +++ b/crates/idx-cli/src/config.rs @@ -14,6 +14,8 @@ use idx_core::error::IdxError; pub struct IdxConfig { pub core: CoreConfig, pub output: OutputFormat, + /// The setting `output` came from, before auto was resolved. + pub output_setting: OutputSetting, pub no_color: bool, } @@ -32,7 +34,7 @@ impl IdxConfig { pub fn load_with_cli(cli: &Cli) -> Result { let mut core = CoreConfig::load()?; core.verbose = cli.verbose > 0; - let mut output = OutputSetting::Auto.resolve(stdout_is_terminal()); + let mut setting = OutputSetting::Auto; let mut no_color = false; let path = config_path()?; @@ -41,8 +43,8 @@ impl IdxConfig { let parsed: FileConfig = toml::from_str(&raw).map_err(|e| IdxError::ConfigError(e.to_string()))?; if let Some(general) = parsed.general { - if let Some(setting) = general.output { - output = setting.resolve(stdout_is_terminal()); + if let Some(file_setting) = general.output { + setting = file_setting; } if let Some(color) = general.color { no_color = !color; @@ -51,25 +53,25 @@ impl IdxConfig { } if let Ok(value) = std::env::var("IDX_OUTPUT") { - let setting = OutputSetting::parse(&value).ok_or_else(|| { + setting = OutputSetting::parse(&value).ok_or_else(|| { IdxError::ConfigError(format!( "invalid IDX_OUTPUT value: '{value}', expected 'auto', 'table', 'json', or 'jsonl'" )) })?; - output = setting.resolve(stdout_is_terminal()); } if let Ok(value) = std::env::var("IDX_NO_COLOR") { no_color = value == "1" || value.eq_ignore_ascii_case("true"); } - if let Some(setting) = cli.output { - output = setting.resolve(stdout_is_terminal()); + if let Some(cli_setting) = cli.output { + setting = cli_setting; } no_color = no_color || cli.no_color || !stdout_is_terminal(); Ok(Self { core, - output, + output: setting.resolve(stdout_is_terminal()), + output_setting: setting, no_color, }) } diff --git a/crates/idx-cli/src/main.rs b/crates/idx-cli/src/main.rs index df02338..f5a7dec 100644 --- a/crates/idx-cli/src/main.rs +++ b/crates/idx-cli/src/main.rs @@ -62,7 +62,7 @@ fn run() -> Result<(), IdxError> { } } Commands::Config(cfg) => { - if let Err(err) = cli::config::handle(cfg, &config.output) { + if let Err(err) = cli::config::handle(cfg, config.output_setting) { emit_error(&err, &config.output); return Err(err); } diff --git a/crates/idx-cli/tests/cli.rs b/crates/idx-cli/tests/cli.rs index 5cdd8ee..0fb9812 100644 --- a/crates/idx-cli/tests/cli.rs +++ b/crates/idx-cli/tests/cli.rs @@ -980,6 +980,43 @@ fn config_set_and_get_provider_round_trip() { .stdout(predicate::str::contains("msn")); } +#[test] +fn config_prints_json_only_when_json_is_asked_for() { + // Piped with auto output, scripts read plain text: `$(idx config path)`. + let root = test_env_dir("config-output-format"); + bin_with_root(&root) + .args(["config", "set", "general.provider", "msn"]) + .assert() + .success() + .stdout("ok\n"); + bin_with_root(&root) + .args(["config", "get", "general.provider"]) + .assert() + .success() + .stdout("msn\n"); + + let flag = run_success_json(bin_with_root(&root).args([ + "-o", + "json", + "config", + "get", + "general.provider", + ])); + assert_eq!( + flag, + serde_json::json!({"key": "general.provider", "value": "msn"}) + ); + let env = run_success_json( + bin_with_root(&root) + .env("IDX_OUTPUT", "json") + .args(["config", "path"]), + ); + assert!( + env["path"].as_str().unwrap().ends_with("config.toml"), + "{env}" + ); +} + #[test] fn config_set_and_get_ownership_db_path_round_trip() { let root = test_env_dir("config-ownership-db-path"); diff --git a/crates/idx-cli/tests/golden/config-get.golden b/crates/idx-cli/tests/golden/config-get.golden index a90ab07..c9ca9de 100644 --- a/crates/idx-cli/tests/golden/config-get.golden +++ b/crates/idx-cli/tests/golden/config-get.golden @@ -1,4 +1,4 @@ exit: 0 --- stdout -{"key":"general.provider","value":"msn"} +msn --- stderr diff --git a/crates/idx-cli/tests/golden/config-init.golden b/crates/idx-cli/tests/golden/config-init.golden index 28a0e2f..ab029c5 100644 --- a/crates/idx-cli/tests/golden/config-init.golden +++ b/crates/idx-cli/tests/golden/config-init.golden @@ -1,4 +1,4 @@ exit: 0 --- stdout -{"path":"/config/idx/config.toml"} +/config/idx/config.toml --- stderr diff --git a/crates/idx-cli/tests/golden/config-path.golden b/crates/idx-cli/tests/golden/config-path.golden index 28a0e2f..ab029c5 100644 --- a/crates/idx-cli/tests/golden/config-path.golden +++ b/crates/idx-cli/tests/golden/config-path.golden @@ -1,4 +1,4 @@ exit: 0 --- stdout -{"path":"/config/idx/config.toml"} +/config/idx/config.toml --- stderr diff --git a/crates/idx-cli/tests/golden/config-set-output-jsonl.golden b/crates/idx-cli/tests/golden/config-set-output-jsonl.golden index 02653fc..aecf1eb 100644 --- a/crates/idx-cli/tests/golden/config-set-output-jsonl.golden +++ b/crates/idx-cli/tests/golden/config-set-output-jsonl.golden @@ -1,4 +1,4 @@ exit: 0 --- stdout -{"key":"general.output","ok":true,"value":"jsonl"} +ok --- stderr diff --git a/crates/idx-cli/tests/golden/config-set.golden b/crates/idx-cli/tests/golden/config-set.golden index 0529834..aecf1eb 100644 --- a/crates/idx-cli/tests/golden/config-set.golden +++ b/crates/idx-cli/tests/golden/config-set.golden @@ -1,4 +1,4 @@ exit: 0 --- stdout -{"key":"general.provider","ok":true,"value":"yahoo"} +ok --- stderr diff --git a/web/src/content/docs/start/config.md b/web/src/content/docs/start/config.md index 2595074..4304b4d 100644 --- a/web/src/content/docs/start/config.md +++ b/web/src/content/docs/start/config.md @@ -22,6 +22,10 @@ idx config get cache.quote_ttl idx config path ``` +`idx config` prints plain text, even when piped, so `$(idx config path)` works +in scripts. Ask for JSON with `-o json`, `IDX_OUTPUT=json`, or +`output = "json"`. + ```toml [general] provider = "msn" # msn | yahoo