Skip to main content

objectiveai_sdk/cli/command/
command.rs

1//! Root-level clap `Command` plus the matching typed `Request` /
2//! `ResponseItem` aggregators and conversions, mirroring the same
3//! pattern every tier `mod.rs` follows for its own subcommands.
4
5#[derive(clap::Subcommand)]
6pub enum Subcommand {
7    Agents {
8        #[command(subcommand)]
9        command: super::agents::Command,
10    },
11    Api {
12        #[command(subcommand)]
13        command: super::api::Command,
14    },
15    Daemon {
16        #[command(subcommand)]
17        command: super::daemon::Command,
18    },
19    Db {
20        #[command(subcommand)]
21        command: super::db::Command,
22    },
23    Functions {
24        #[command(subcommand)]
25        command: super::functions::Command,
26    },
27    /// Kill every process holding a lock anywhere under the configured
28    /// `OBJECTIVEAI_DIR` — the blunt whole-tree sweep.
29    KillAll(super::kill_all::Command),
30    Mcp {
31        #[command(subcommand)]
32        command: super::mcp::Command,
33    },
34    Plugins {
35        #[command(subcommand)]
36        command: super::plugins::Command,
37    },
38    /// Run a Python snippet and return its output as JSON.
39    Python(super::python::Command),
40    Swarms {
41        #[command(subcommand)]
42        command: super::swarms::Command,
43    },
44    Tools {
45        #[command(subcommand)]
46        command: super::tools::Command,
47    },
48    Update(super::update::Command),
49    Viewer {
50        #[command(subcommand)]
51        command: super::viewer::Command,
52    },
53}
54
55#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
56#[serde(untagged)]
57#[schemars(rename = "cli.command.Request")]
58pub enum Request {
59    #[schemars(title = "Agents")]
60    Agents(super::agents::Request),
61    #[schemars(title = "Api")]
62    Api(super::api::Request),
63    #[schemars(title = "Daemon")]
64    Daemon(super::daemon::Request),
65    #[schemars(title = "Db")]
66    Db(super::db::Request),
67    #[schemars(title = "Functions")]
68    Functions(super::functions::Request),
69    #[schemars(title = "KillAll")]
70    KillAll(super::kill_all::Request),
71    #[schemars(title = "KillAllRequestSchema")]
72    KillAllRequestSchema(super::kill_all::request_schema::Request),
73    #[schemars(title = "KillAllResponseSchema")]
74    KillAllResponseSchema(super::kill_all::response_schema::Request),
75    #[schemars(title = "Mcp")]
76    Mcp(super::mcp::Request),
77    #[schemars(title = "Plugins")]
78    Plugins(super::plugins::Request),
79    #[schemars(title = "Python")]
80    Python(super::python::Request),
81    #[schemars(title = "PythonRequestSchema")]
82    PythonRequestSchema(super::python::request_schema::Request),
83    #[schemars(title = "PythonResponseSchema")]
84    PythonResponseSchema(super::python::response_schema::Request),
85    #[schemars(title = "Swarms")]
86    Swarms(super::swarms::Request),
87    #[schemars(title = "Tools")]
88    Tools(super::tools::Request),
89    #[schemars(title = "Update")]
90    Update(super::update::Request),
91    #[schemars(title = "UpdateRequestSchema")]
92    UpdateRequestSchema(super::update::request_schema::Request),
93    #[schemars(title = "UpdateResponseSchema")]
94    UpdateResponseSchema(super::update::response_schema::Request),
95    #[schemars(title = "Viewer")]
96    Viewer(super::viewer::Request),
97}
98
99// Exempt from json-schema coverage: the aggregate's transitive
100// expansion spans the whole command tree, which downstream
101// generated TypeScript cannot emit declarations for (TS7056), and
102// no consumer uses the aggregate schema — leaf schemas cover the
103// wire.
104#[objectiveai_sdk_macros::json_schema_ignore]
105#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
106#[schemars(rename = "cli.command.ResponseItem")]
107#[serde(untagged)]
108pub enum ResponseItem {
109    #[schemars(title = "Agents")]
110    Agents(super::agents::ResponseItem),
111    #[schemars(title = "Api")]
112    Api(super::api::Response),
113    #[schemars(title = "Daemon")]
114    Daemon(super::daemon::ResponseItem),
115    #[schemars(title = "Db")]
116    Db(super::db::ResponseItem),
117    #[schemars(title = "Functions")]
118    Functions(super::functions::ResponseItem),
119    #[schemars(title = "KillAll")]
120    KillAll(super::kill_all::Response),
121    #[schemars(title = "KillAllRequestSchema")]
122    KillAllRequestSchema(super::kill_all::request_schema::Response),
123    #[schemars(title = "KillAllResponseSchema")]
124    KillAllResponseSchema(super::kill_all::response_schema::Response),
125    #[schemars(title = "Mcp")]
126    Mcp(super::mcp::Response),
127    #[schemars(title = "Plugins")]
128    Plugins(super::plugins::ResponseItem),
129    #[schemars(title = "Python")]
130    Python(serde_json::Value),
131    #[schemars(title = "PythonRequestSchema")]
132    PythonRequestSchema(super::python::request_schema::Response),
133    #[schemars(title = "PythonResponseSchema")]
134    PythonResponseSchema(super::python::response_schema::Response),
135    #[schemars(title = "Swarms")]
136    Swarms(super::swarms::ResponseItem),
137    #[schemars(title = "Tools")]
138    Tools(super::tools::ResponseItem),
139    #[schemars(title = "Update")]
140    Update(super::update::ResponseItem),
141    #[schemars(title = "UpdateRequestSchema")]
142    UpdateRequestSchema(super::update::request_schema::Response),
143    #[schemars(title = "UpdateResponseSchema")]
144    UpdateResponseSchema(super::update::response_schema::Response),
145    #[schemars(title = "Viewer")]
146    Viewer(super::viewer::Response),
147}
148
149#[cfg(feature = "mcp")]
150impl super::CommandResponse for ResponseItem {
151    fn into_mcp(self) -> super::McpResponseItem {
152        match self {
153            ResponseItem::Agents(v) => v.into_mcp(),
154            ResponseItem::Api(v) => v.into_mcp(),
155            ResponseItem::Daemon(v) => v.into_mcp(),
156            ResponseItem::Db(v) => v.into_mcp(),
157            ResponseItem::Functions(v) => v.into_mcp(),
158            ResponseItem::KillAll(v) => v.into_mcp(),
159            ResponseItem::KillAllRequestSchema(v) => v.into_mcp(),
160            ResponseItem::KillAllResponseSchema(v) => v.into_mcp(),
161            ResponseItem::Mcp(v) => v.into_mcp(),
162            ResponseItem::Plugins(v) => v.into_mcp(),
163            ResponseItem::Python(v) => v.into_mcp(),
164            ResponseItem::PythonRequestSchema(v) => v.into_mcp(),
165            ResponseItem::PythonResponseSchema(v) => v.into_mcp(),
166            ResponseItem::Swarms(v) => v.into_mcp(),
167            ResponseItem::Tools(v) => v.into_mcp(),
168            ResponseItem::Update(v) => v.into_mcp(),
169            ResponseItem::UpdateRequestSchema(v) => v.into_mcp(),
170            ResponseItem::UpdateResponseSchema(v) => v.into_mcp(),
171            ResponseItem::Viewer(v) => v.into_mcp(),
172        }
173    }
174}
175
176impl TryFrom<Subcommand> for Request {
177    type Error = super::FromArgsError;
178    fn try_from(command: Subcommand) -> Result<Self, Self::Error> {
179        match command {
180            Subcommand::Agents { command } =>
181                Ok(Request::Agents(super::agents::Request::try_from(command)?)),
182            Subcommand::Api { command } =>
183                Ok(Request::Api(super::api::Request::try_from(command)?)),
184            Subcommand::Daemon { command } =>
185                Ok(Request::Daemon(super::daemon::Request::try_from(command)?)),
186            Subcommand::Db { command } =>
187                Ok(Request::Db(super::db::Request::try_from(command)?)),
188            Subcommand::Functions { command } =>
189                Ok(Request::Functions(super::functions::Request::try_from(command)?)),
190            Subcommand::KillAll(cmd) => match cmd.schema {
191                None => Ok(Request::KillAll(super::kill_all::Request::try_from(cmd.args)?)),
192                Some(super::kill_all::Schema::RequestSchema(args)) =>
193                    Ok(Request::KillAllRequestSchema(super::kill_all::request_schema::Request::try_from(args)?)),
194                Some(super::kill_all::Schema::ResponseSchema(args)) =>
195                    Ok(Request::KillAllResponseSchema(super::kill_all::response_schema::Request::try_from(args)?)),
196            },
197            Subcommand::Mcp { command } =>
198                Ok(Request::Mcp(super::mcp::Request::try_from(command)?)),
199            Subcommand::Plugins { command } =>
200                Ok(Request::Plugins(super::plugins::Request::try_from(command)?)),
201            Subcommand::Python(cmd) => match cmd.schema {
202                None => Ok(Request::Python(super::python::Request::try_from(cmd.args)?)),
203                Some(super::python::Schema::RequestSchema(args)) =>
204                    Ok(Request::PythonRequestSchema(super::python::request_schema::Request::try_from(args)?)),
205                Some(super::python::Schema::ResponseSchema(args)) =>
206                    Ok(Request::PythonResponseSchema(super::python::response_schema::Request::try_from(args)?)),
207            },
208            Subcommand::Swarms { command } =>
209                Ok(Request::Swarms(super::swarms::Request::try_from(command)?)),
210            Subcommand::Tools { command } =>
211                Ok(Request::Tools(super::tools::Request::try_from(command)?)),
212            Subcommand::Update(cmd) => match cmd.schema {
213                None => Ok(Request::Update(super::update::Request::try_from(cmd.args)?)),
214                Some(super::update::Schema::RequestSchema(args)) =>
215                    Ok(Request::UpdateRequestSchema(super::update::request_schema::Request::try_from(args)?)),
216                Some(super::update::Schema::ResponseSchema(args)) =>
217                    Ok(Request::UpdateResponseSchema(super::update::response_schema::Request::try_from(args)?)),
218            },
219            Subcommand::Viewer { command } =>
220                Ok(Request::Viewer(super::viewer::Request::try_from(command)?)),
221        }
222    }
223}
224
225impl TryFrom<Command> for Request {
226    type Error = super::FromArgsError;
227    fn try_from(command: Command) -> Result<Self, Self::Error> {
228        match (command.request, command.command) {
229            // `--request <json>`: deserialize straight into the aggregate
230            // Request (serde-tagged on `path_type`) — the same wire shape the
231            // SDKs already serialize. Mutually exclusive with a subcommand
232            // (clap enforces it), so the `_` arm can't carry a command.
233            (Some(json), _) => {
234                let de = &mut serde_json::Deserializer::from_str(&json);
235                serde_path_to_error::deserialize(de)
236                    .map_err(|e| super::FromArgsError::json("request", e))
237            }
238            (None, Some(subcommand)) => Request::try_from(subcommand),
239            // Unreachable in practice: `arg_required_else_help` turns a bare
240            // invocation into a clap help error before conversion.
241            (None, None) => Err(super::FromArgsError::path_parse(
242                "command",
243                "a command path or --request is required".to_string(),
244            )),
245        }
246    }
247}
248
249impl super::CommandRequest for Request {
250    fn request_base(&self) -> &crate::cli::command::RequestBase {
251        match self {
252            Request::Agents(inner) => inner.request_base(),
253            Request::Api(inner) => inner.request_base(),
254            Request::Daemon(inner) => inner.request_base(),
255            Request::Db(inner) => inner.request_base(),
256            Request::Functions(inner) => inner.request_base(),
257            Request::KillAll(inner) => inner.request_base(),
258            Request::KillAllRequestSchema(inner) => inner.request_base(),
259            Request::KillAllResponseSchema(inner) => inner.request_base(),
260            Request::Mcp(inner) => inner.request_base(),
261            Request::Plugins(inner) => inner.request_base(),
262            Request::Python(inner) => inner.request_base(),
263            Request::PythonRequestSchema(inner) => inner.request_base(),
264            Request::PythonResponseSchema(inner) => inner.request_base(),
265            Request::Swarms(inner) => inner.request_base(),
266            Request::Tools(inner) => inner.request_base(),
267            Request::Update(inner) => inner.request_base(),
268            Request::UpdateRequestSchema(inner) => inner.request_base(),
269            Request::UpdateResponseSchema(inner) => inner.request_base(),
270            Request::Viewer(inner) => inner.request_base(),
271        }
272    }
273
274    fn request_base_mut(&mut self) -> Option<&mut crate::cli::command::RequestBase> {
275        match self {
276            Request::Agents(inner) => inner.request_base_mut(),
277            Request::Api(inner) => inner.request_base_mut(),
278            Request::Daemon(inner) => inner.request_base_mut(),
279            Request::Db(inner) => inner.request_base_mut(),
280            Request::Functions(inner) => inner.request_base_mut(),
281            Request::KillAll(inner) => inner.request_base_mut(),
282            Request::KillAllRequestSchema(inner) => inner.request_base_mut(),
283            Request::KillAllResponseSchema(inner) => inner.request_base_mut(),
284            Request::Mcp(inner) => inner.request_base_mut(),
285            Request::Plugins(inner) => inner.request_base_mut(),
286            Request::Python(inner) => inner.request_base_mut(),
287            Request::PythonRequestSchema(inner) => inner.request_base_mut(),
288            Request::PythonResponseSchema(inner) => inner.request_base_mut(),
289            Request::Swarms(inner) => inner.request_base_mut(),
290            Request::Tools(inner) => inner.request_base_mut(),
291            Request::Update(inner) => inner.request_base_mut(),
292            Request::UpdateRequestSchema(inner) => inner.request_base_mut(),
293            Request::UpdateResponseSchema(inner) => inner.request_base_mut(),
294            Request::Viewer(inner) => inner.request_base_mut(),
295        }
296    }
297}
298
299#[cfg(feature = "cli-executor")]
300pub async fn execute<E: super::CommandExecutor>(
301    executor: &E,
302    request: Request,
303
304        agent_arguments: Option<&crate::cli::command::AgentArguments>,
305    ) -> Result<
306    std::pin::Pin<Box<dyn futures::Stream<Item = Result<ResponseItem, E::Error>> + Send>>,
307    E::Error,
308> {
309    use futures::StreamExt;
310    let stream: std::pin::Pin<Box<dyn futures::Stream<Item = Result<ResponseItem, E::Error>> + Send>> =
311        match request {
312            Request::Agents(req) => {
313                let inner = super::agents::execute(executor, req, agent_arguments).await?;
314                Box::pin(inner.map(|r| r.map(ResponseItem::Agents)))
315            }
316            Request::Api(req) => {
317                let inner = super::api::execute(executor, req, agent_arguments).await?;
318                Box::pin(inner.map(|r| r.map(ResponseItem::Api)))
319            }
320            Request::Daemon(req) => {
321                let inner = super::daemon::execute(executor, req, agent_arguments).await?;
322                Box::pin(inner.map(|r| r.map(ResponseItem::Daemon)))
323            }
324            Request::Db(req) => {
325                let inner = super::db::execute(executor, req, agent_arguments).await?;
326                Box::pin(inner.map(|r| r.map(ResponseItem::Db)))
327            }
328            Request::Functions(req) => {
329                let inner = super::functions::execute(executor, req, agent_arguments).await?;
330                Box::pin(inner.map(|r| r.map(ResponseItem::Functions)))
331            }
332            Request::KillAll(req) => {
333                let value = super::kill_all::execute(executor, req, agent_arguments).await?;
334                Box::pin(super::StreamOnce::new(Ok(ResponseItem::KillAll(value))))
335            }
336            Request::KillAllRequestSchema(req) => {
337                let value = super::kill_all::request_schema::execute(executor, req, agent_arguments).await?;
338                Box::pin(super::StreamOnce::new(Ok(ResponseItem::KillAllRequestSchema(value))))
339            }
340            Request::KillAllResponseSchema(req) => {
341                let value = super::kill_all::response_schema::execute(executor, req, agent_arguments).await?;
342                Box::pin(super::StreamOnce::new(Ok(ResponseItem::KillAllResponseSchema(value))))
343            }
344            Request::Mcp(req) => {
345                let inner = super::mcp::execute(executor, req, agent_arguments).await?;
346                Box::pin(inner.map(|r| r.map(ResponseItem::Mcp)))
347            }
348            Request::Plugins(req) => {
349                let inner = super::plugins::execute(executor, req, agent_arguments).await?;
350                Box::pin(inner.map(|r| r.map(ResponseItem::Plugins)))
351            }
352            Request::Python(req) => {
353                let value = super::python::execute(executor, req, agent_arguments).await?;
354                Box::pin(super::StreamOnce::new(Ok(ResponseItem::Python(value))))
355            }
356            Request::PythonRequestSchema(req) => {
357                let value = super::python::request_schema::execute(executor, req, agent_arguments).await?;
358                Box::pin(super::StreamOnce::new(Ok(ResponseItem::PythonRequestSchema(value))))
359            }
360            Request::PythonResponseSchema(req) => {
361                let value = super::python::response_schema::execute(executor, req, agent_arguments).await?;
362                Box::pin(super::StreamOnce::new(Ok(ResponseItem::PythonResponseSchema(value))))
363            }
364            Request::Swarms(req) => {
365                let inner = super::swarms::execute(executor, req, agent_arguments).await?;
366                Box::pin(inner.map(|r| r.map(ResponseItem::Swarms)))
367            }
368            Request::Tools(req) => {
369                let inner = super::tools::execute(executor, req, agent_arguments).await?;
370                Box::pin(inner.map(|r| r.map(ResponseItem::Tools)))
371            }
372            Request::Update(req) => {
373                let inner = super::update::execute(executor, req, agent_arguments).await?;
374                Box::pin(inner.map(|r| r.map(ResponseItem::Update)))
375            }
376            Request::UpdateRequestSchema(req) => {
377                let value = super::update::request_schema::execute(executor, req, agent_arguments).await?;
378                Box::pin(super::StreamOnce::new(Ok(ResponseItem::UpdateRequestSchema(value))))
379            }
380            Request::UpdateResponseSchema(req) => {
381                let value = super::update::response_schema::execute(executor, req, agent_arguments).await?;
382                Box::pin(super::StreamOnce::new(Ok(ResponseItem::UpdateResponseSchema(value))))
383            }
384            Request::Viewer(req) => {
385                let inner = super::viewer::execute(executor, req, agent_arguments).await?;
386                Box::pin(inner.map(|r| r.map(ResponseItem::Viewer)))
387            }
388        };
389    Ok(stream)
390}
391
392#[cfg(feature = "cli-executor")]
393pub async fn execute_transform<E: super::CommandExecutor>(
394    executor: &E,
395    request: Request,
396    transform: crate::cli::command::Transform,
397
398        agent_arguments: Option<&crate::cli::command::AgentArguments>,
399    ) -> Result<
400    std::pin::Pin<Box<dyn futures::Stream<Item = Result<serde_json::Value, E::Error>> + Send>>,
401    E::Error,
402> {
403    let stream: std::pin::Pin<Box<dyn futures::Stream<Item = Result<serde_json::Value, E::Error>> + Send>> =
404        match request {
405            Request::Agents(req) => {
406                let inner = super::agents::execute_transform(executor, req, transform, agent_arguments).await?;
407                Box::pin(inner)
408            }
409            Request::Api(req) => {
410                let inner = super::api::execute_transform(executor, req, transform, agent_arguments).await?;
411                Box::pin(inner)
412            }
413            Request::Daemon(req) => {
414                let inner = super::daemon::execute_transform(executor, req, transform, agent_arguments).await?;
415                Box::pin(inner)
416            }
417            Request::Db(req) => {
418                let inner = super::db::execute_transform(executor, req, transform, agent_arguments).await?;
419                Box::pin(inner)
420            }
421            Request::Functions(req) => {
422                let inner = super::functions::execute_transform(executor, req, transform, agent_arguments).await?;
423                Box::pin(inner)
424            }
425            Request::KillAll(req) => {
426                let value = super::kill_all::execute_transform(executor, req, transform, agent_arguments).await?;
427                Box::pin(super::StreamOnce::new(Ok(value)))
428            }
429            Request::KillAllRequestSchema(req) => {
430                let value = super::kill_all::request_schema::execute_transform(executor, req, transform, agent_arguments).await?;
431                Box::pin(super::StreamOnce::new(Ok(value)))
432            }
433            Request::KillAllResponseSchema(req) => {
434                let value = super::kill_all::response_schema::execute_transform(executor, req, transform, agent_arguments).await?;
435                Box::pin(super::StreamOnce::new(Ok(value)))
436            }
437            Request::Mcp(req) => {
438                let inner = super::mcp::execute_transform(executor, req, transform, agent_arguments).await?;
439                Box::pin(inner)
440            }
441            Request::Plugins(req) => {
442                let inner = super::plugins::execute_transform(executor, req, transform, agent_arguments).await?;
443                Box::pin(inner)
444            }
445            Request::Python(req) => {
446                let value = super::python::execute_transform(executor, req, transform, agent_arguments).await?;
447                Box::pin(super::StreamOnce::new(Ok(value)))
448            }
449            Request::PythonRequestSchema(req) => {
450                let value = super::python::request_schema::execute_transform(executor, req, transform, agent_arguments).await?;
451                Box::pin(super::StreamOnce::new(Ok(value)))
452            }
453            Request::PythonResponseSchema(req) => {
454                let value = super::python::response_schema::execute_transform(executor, req, transform, agent_arguments).await?;
455                Box::pin(super::StreamOnce::new(Ok(value)))
456            }
457            Request::Swarms(req) => {
458                let inner = super::swarms::execute_transform(executor, req, transform, agent_arguments).await?;
459                Box::pin(inner)
460            }
461            Request::Tools(req) => {
462                let inner = super::tools::execute_transform(executor, req, transform, agent_arguments).await?;
463                Box::pin(inner)
464            }
465            Request::Update(req) => {
466                let inner = super::update::execute_transform(executor, req, transform, agent_arguments).await?;
467                Box::pin(inner)
468            }
469            Request::UpdateRequestSchema(req) => {
470                let value = super::update::request_schema::execute_transform(executor, req, transform, agent_arguments).await?;
471                Box::pin(super::StreamOnce::new(Ok(value)))
472            }
473            Request::UpdateResponseSchema(req) => {
474                let value = super::update::response_schema::execute_transform(executor, req, transform, agent_arguments).await?;
475                Box::pin(super::StreamOnce::new(Ok(value)))
476            }
477            Request::Viewer(req) => {
478                let inner = super::viewer::execute_transform(executor, req, transform, agent_arguments).await?;
479                Box::pin(inner)
480            }
481        };
482    Ok(stream)
483}
484
485/// Parse an argv slice into a typed [`Request`]. Accepts either
486/// shape:
487///
488/// - With a program-name prefix (`["objectiveai", "agents", "list"]`)
489///   — matches `std::env::args()` and binary entry-point usage.
490/// - Without one (`["agents", "list"]`) — matches
491///   [`super::CommandRequest::into_command`]'s output shape and the
492///   "command-only" shape MCP callers send.
493///
494/// If `args[0]` is `"objectiveai"` we pass it straight through;
495/// otherwise we prepend so clap (which always treats argv[0] as the
496/// program name) sees a well-formed argv. Hides clap behind the SDK
497/// boundary so downstream crates can dispatch arbitrary argv without
498/// taking a clap dep themselves.
499pub fn parse_request(args: &[String]) -> Result<Request, ParseError> {
500    let command = if args.first().map(String::as_str) == Some("objectiveai") {
501        <Command as clap::Parser>::try_parse_from(args)?
502    } else {
503        let argv = std::iter::once("objectiveai".to_string())
504            .chain(args.iter().cloned());
505        <Command as clap::Parser>::try_parse_from(argv)?
506    };
507    Ok(Request::try_from(command)?)
508}
509
510/// Top-level CLI parser: a command path, OR `--request <json>` to execute a
511/// JSON `CliCommandRequest` directly. The two are mutually exclusive
512/// (`args_conflicts_with_subcommands`, enforced natively by clap); a bare
513/// invocation prints help (`arg_required_else_help`). `--request` is a
514/// legitimate but unadvertised programmatic entry point.
515#[derive(clap::Parser)]
516#[command(
517    name = "objectiveai",
518    args_conflicts_with_subcommands = true,
519    arg_required_else_help = true
520)]
521pub struct Command {
522    /// Execute this JSON `CliCommandRequest` instead of a command path.
523    /// Mutually exclusive with any command.
524    #[arg(long, value_name = "JSON")]
525    pub request: Option<String>,
526    #[command(subcommand)]
527    pub command: Option<Subcommand>,
528}
529
530/// Error from [`parse_request`]. Either clap rejected the argv
531/// ([`ParseError::Clap`] — `--help`, unknown subcommand, missing
532/// required arg, etc.) or the typed `Command` couldn't be lowered
533/// into a `Request` ([`ParseError::FromArgs`] — inline body JSON
534/// failed to parse, etc.).
535#[derive(Debug)]
536pub enum ParseError {
537    Clap(clap::Error),
538    FromArgs(super::FromArgsError),
539}
540
541impl std::fmt::Display for ParseError {
542    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
543        match self {
544            ParseError::Clap(e) => write!(f, "{e}"),
545            ParseError::FromArgs(e) => write!(f, "{e}"),
546        }
547    }
548}
549
550impl std::error::Error for ParseError {}
551
552impl From<clap::Error> for ParseError {
553    fn from(e: clap::Error) -> Self {
554        ParseError::Clap(e)
555    }
556}
557
558impl From<super::FromArgsError> for ParseError {
559    fn from(e: super::FromArgsError) -> Self {
560        ParseError::FromArgs(e)
561    }
562}