diff --git a/crates/openshell-cli/src/main.rs b/crates/openshell-cli/src/main.rs index 3a8c344d3..729603c82 100644 --- a/crates/openshell-cli/src/main.rs +++ b/crates/openshell-cli/src/main.rs @@ -1039,7 +1039,11 @@ enum GatewayCommands { /// Prints a table of all registered gateways with their endpoint, type, /// and authentication mode. The active gateway is marked with `*`. #[command(help_template = LEAF_HELP_TEMPLATE, next_help_heading = "FLAGS")] - List, + List { + /// Output format. + #[arg(short = 'o', long = "output", value_enum, default_value_t = OutputFormat::Table)] + output: OutputFormat, + }, } // ----------------------------------------------------------------------- @@ -1942,8 +1946,8 @@ async fn main() -> Result<()> { .unwrap_or_else(|| "openshell".to_string()); run::gateway_admin_info(&name)?; } - GatewayCommands::List => { - run::gateway_list(&cli.gateway)?; + GatewayCommands::List { output } => { + run::gateway_list(&cli.gateway, output.as_str())?; } }, @@ -3686,6 +3690,51 @@ mod tests { assert!(result.is_err(), "--names and -o json should conflict"); } + #[test] + fn gateway_list_default_output_is_table() { + let cli = Cli::try_parse_from(["openshell", "gateway", "list"]) + .expect("gateway list should parse"); + + assert!(matches!( + cli.command, + Some(Commands::Gateway { + command: Some(GatewayCommands::List { + output: OutputFormat::Table, + }) + }) + )); + } + + #[test] + fn gateway_list_accepts_output_json() { + let cli = Cli::try_parse_from(["openshell", "gateway", "list", "-o", "json"]) + .expect("gateway list -o json should parse"); + + assert!(matches!( + cli.command, + Some(Commands::Gateway { + command: Some(GatewayCommands::List { + output: OutputFormat::Json, + }) + }) + )); + } + + #[test] + fn gateway_list_accepts_output_yaml() { + let cli = Cli::try_parse_from(["openshell", "gateway", "list", "-o", "yaml"]) + .expect("gateway list -o yaml should parse"); + + assert!(matches!( + cli.command, + Some(Commands::Gateway { + command: Some(GatewayCommands::List { + output: OutputFormat::Yaml, + }) + }) + )); + } + #[test] fn provider_create_accepts_custom_profile_type_ids() { let cli = Cli::try_parse_from([ diff --git a/crates/openshell-cli/src/run.rs b/crates/openshell-cli/src/run.rs index 2e3cb0531..d30cc3363 100644 --- a/crates/openshell-cli/src/run.rs +++ b/crates/openshell-cli/src/run.rs @@ -802,7 +802,7 @@ where let gateways = list_gateways()?; if gateways.is_empty() || !interactive { - gateway_list(gateway_flag)?; + gateway_list(gateway_flag, "table")?; if !gateways.is_empty() { eprintln!(); eprintln!( @@ -1256,10 +1256,34 @@ pub fn gateway_logout(name: &str) -> Result<()> { } /// List all registered gateways. -pub fn gateway_list(gateway_flag: &Option) -> Result<()> { +pub fn gateway_list(gateway_flag: &Option, output: &str) -> Result<()> { let gateways = list_gateways()?; let active = gateway_flag.clone().or_else(load_active_gateway); + match output { + "json" => { + let items: Vec = gateways + .iter() + .map(|g| gateway_to_json(g, &active)) + .collect(); + println!( + "{}", + serde_json::to_string_pretty(&items).into_diagnostic()? + ); + return Ok(()); + } + "yaml" => { + let items: Vec = gateways + .iter() + .map(|g| gateway_to_json(g, &active)) + .collect(); + print!("{}", serde_yml::to_string(&items).into_diagnostic()?); + return Ok(()); + } + "table" => {} + _ => return Err(miette!("unsupported output format: {output}")), + } + if gateways.is_empty() { println!("No gateways found."); println!(); @@ -1319,6 +1343,16 @@ pub fn gateway_list(gateway_flag: &Option) -> Result<()> { Ok(()) } +fn gateway_to_json(gateway: &GatewayMetadata, active: &Option) -> serde_json::Value { + serde_json::json!({ + "name": gateway.name, + "endpoint": gateway.gateway_endpoint, + "type": gateway_type_label(gateway), + "auth": gateway_auth_label(gateway), + "active": active.as_deref() == Some(&gateway.name), + }) +} + async fn http_health_check(server: &str, tls: &TlsOptions) -> Result> { let base = server.trim_end_matches('/'); let uri: hyper::Uri = format!("{base}/healthz").parse().into_diagnostic()?;