use clap::CommandFactory; use serde_json::Value; use crate::cli::{Cli, Sub}; use crate::{client, mapping}; /// Entry point: returns the process exit code. pub async fn run(cli: Cli) -> i32 { // Completions are local — no daemon. if let Sub::Completions { shell } = cli.cmd { let mut cmd = Cli::command(); clap_complete::generate(shell, &mut cmd, "spacesh", &mut std::io::stdout()); return 0; } // notify is best-effort: never fails the caller. if let Sub::Notify { .. } = &cli.cmd { let _ = client::notify(mapping::to_cmd(cli.cmd)).await; return 0; } let is_status = matches!(cli.cmd, Sub::Status); let cmd = mapping::to_cmd(cli.cmd); match client::request(cmd).await { Ok(data) => { if cli.json { println!("{}", serde_json::to_string_pretty(&data).unwrap_or_else(|_| "null".into())); } else if is_status { print_status(&data); } else { print_human(&data); } 0 } Err(e) => { if cli.json { println!("{}", serde_json::json!({ "ok": false, "error": e.to_string() })); } else { eprintln!("{e}"); } 1 } } } /// Human render for non-status commands: surface the salient id, else "ok". fn print_human(data: &Value) { if let Some(id) = data.get("workspace_id").and_then(|v| v.as_str()) { println!("{id}"); } else if let Some(id) = data.get("surface_id").and_then(|v| v.as_str()) { println!("{id}"); } else if let Some(id) = data.get("group_id").and_then(|v| v.as_str()) { println!("{id}"); } else if let Some(ids) = data.get("surface_ids").and_then(|v| v.as_array()) { for id in ids { if let Some(s) = id.as_str() { println!("{s}"); } } } else { println!("ok"); } } /// Compact table for `status`. fn print_status(data: &Value) { let empty = vec![]; let workspaces = data.get("workspaces").and_then(|v| v.as_array()).unwrap_or(&empty); if workspaces.is_empty() { println!("(no workspaces)"); return; } for w in workspaces { let name = w.get("name").and_then(|v| v.as_str()).unwrap_or("?"); let id = w.get("id").and_then(|v| v.as_str()).unwrap_or("?"); let unread = w.get("unread").and_then(|v| v.as_bool()).unwrap_or(false); println!("{} ({}){}", name, id, if unread { " *" } else { "" }); if let Some(surfaces) = w.get("surfaces").and_then(|v| v.as_object()) { for (sid, sv) in surfaces { let running = sv.get("running").and_then(|v| v.as_bool()).unwrap_or(false); let state = sv.get("state").and_then(|v| v.as_str()).unwrap_or("idle"); let agent = sv.get("spec").and_then(|s| s.get("agent_label")).and_then(|v| v.as_str()).unwrap_or("shell"); let life = if running { "running" } else { "stopped" }; println!(" {sid} {agent:<8} {life:<8} {state}"); } } } }