1use anyhow::{anyhow, Result};
2use clap::{Args, Subcommand};
3
4use crate::client::RommClient;
5use crate::commands::OutputFormat;
6
7#[derive(Args, Debug)]
9pub struct ApiCommand {
10 #[command(subcommand)]
11 pub action: Option<ApiAction>,
12
13 pub method: Option<String>,
15
16 pub path: Option<String>,
18
19 #[arg(long = "query", global = true)]
21 pub query: Vec<String>,
22
23 #[arg(long, global = true)]
25 pub data: Option<String>,
26}
27
28#[derive(Subcommand, Debug)]
29pub enum ApiAction {
30 Call {
32 method: String,
34 path: String,
36 },
37 Get {
39 path: String,
41 },
42 Post {
44 path: String,
46 },
47}
48
49pub async fn handle(cmd: ApiCommand, client: &RommClient, format: OutputFormat) -> Result<()> {
50 let (method, path) = match cmd.action {
51 Some(ApiAction::Call { method, path }) => (method, path),
52 Some(ApiAction::Get { path }) => ("GET".to_string(), path),
53 Some(ApiAction::Post { path }) => ("POST".to_string(), path),
54 None => {
55 let m = cmd
56 .method
57 .ok_or_else(|| anyhow!("Method is required (e.g. 'api call GET /api/roms')"))?;
58 let p = cmd
59 .path
60 .ok_or_else(|| anyhow!("Path is required (e.g. 'api call GET /api/roms')"))?;
61 (m, p)
62 }
63 };
64
65 let mut query_pairs = Vec::new();
66 for q in &cmd.query {
67 if let Some((k, v)) = q.split_once('=') {
68 query_pairs.push((k.to_string(), v.to_string()));
69 } else {
70 eprintln!(
71 "warning: ignoring malformed --query value {:?}; expected key=value",
72 q
73 );
74 }
75 }
76
77 let body = if let Some(data) = &cmd.data {
78 Some(serde_json::from_str(data)?)
79 } else {
80 None
81 };
82
83 let value = client
84 .request_json(&method, &path, &query_pairs, body)
85 .await?;
86
87 match format {
88 OutputFormat::Json => {
89 println!("{}", serde_json::to_string_pretty(&value)?);
90 }
91 OutputFormat::Text => {
92 println!("{}", serde_json::to_string_pretty(&value)?);
93 }
94 }
95 Ok(())
96}