const API_SCHEMA_JSON: &str = include_str!("../../docs/next/api/herdr-api.schema.json"); use crate::api::schema::{EmptyParams, Method, Request}; pub(super) fn run_api_command(args: &[String]) -> std::io::Result { let Some(subcommand) = args.first().map(String::as_str) else { print_api_help(); return Ok(2); }; match subcommand { "schema" => api_schema(&args[1..]), "snapshot" => api_snapshot(&args[1..]), "help" | "--help" | "-h" => { print_api_help(); Ok(0) } _ => { print_api_help(); Ok(2) } } } fn api_schema(args: &[String]) -> std::io::Result { match args { [] => { print!("{}", schema_summary_text()?); } [flag] if flag == "--json" => { print!("{API_SCHEMA_JSON}"); } [flag, path] if flag == "--output" => { write_schema_file(std::path::Path::new(path))?; println!("wrote API schema to {path}"); } [flag] if flag == "--output" => { eprintln!("missing value for --output"); return Ok(2); } [flag] if matches!(flag.as_str(), "help" | "--help" | "-h") => { print_api_schema_help(); } [other] if other.starts_with('-') => { eprintln!("unknown option: {other}"); return Ok(2); } _ => { print_api_schema_help(); return Ok(2); } } Ok(0) } fn api_snapshot(args: &[String]) -> std::io::Result { if !args.is_empty() { eprintln!("usage: herdr api snapshot"); return Ok(2); } super::print_response(&super::send_request(&Request { id: "cli:api:snapshot".into(), method: Method::SessionSnapshot(EmptyParams::default()), })?) } fn write_schema_file(path: &std::path::Path) -> std::io::Result<()> { std::fs::write(path, API_SCHEMA_JSON) } fn schema_summary_text() -> std::io::Result { let value: serde_json::Value = serde_json::from_str(API_SCHEMA_JSON)?; let protocol = value .get("protocol") .and_then(serde_json::Value::as_u64) .ok_or_else(|| std::io::Error::other("API schema is missing protocol"))?; let schema_version = value .get("schema_version") .and_then(serde_json::Value::as_u64) .ok_or_else(|| std::io::Error::other("API schema is missing schema_version"))?; let mut schemas: Vec<&str> = value .get("schemas") .and_then(serde_json::Value::as_object) .ok_or_else(|| std::io::Error::other("API schema is missing schemas"))? .keys() .map(String::as_str) .collect(); schemas.sort(); Ok(format!( "Herdr API schema\nprotocol: {}\nschema_version: {}\nschemas: {}\n\nUse `herdr api schema --json` to print the full schema.\nUse `herdr api schema --output PATH` to write it to a file.\n", protocol, schema_version, schemas.join(", ") )) } fn print_api_help() { eprintln!("herdr api commands:"); eprintln!(" herdr api snapshot"); eprintln!(" herdr api schema [--json | --output PATH]"); } fn print_api_schema_help() { eprintln!("usage: herdr api schema [--json | --output PATH]"); } #[cfg(test)] mod tests { #[test] fn schema_summary_text_stays_human_sized() { let text = super::schema_summary_text().unwrap(); assert!(text.contains("Herdr API schema")); assert!(text.contains("Use `herdr api schema --json`")); assert!(text.len() < 400); } }