diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-01-22 08:32:29 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-01-22 08:32:29 +0800 |
| commit | aca8b408755f9041da9ee083c625de2a8d8c6785 (patch) | |
| tree | 5747d389d5218ccf39e2153ae1346f7b5bfe8fb8 /src/subcmd/cmd.rs | |
| parent | 0d614f3e2104e9b840ebc7e53a6caa6af1671636 (diff) | |
Refactor CLI command processing with new architecture
Diffstat (limited to 'src/subcmd/cmd.rs')
| -rw-r--r-- | src/subcmd/cmd.rs | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/src/subcmd/cmd.rs b/src/subcmd/cmd.rs new file mode 100644 index 0000000..10ad893 --- /dev/null +++ b/src/subcmd/cmd.rs @@ -0,0 +1,85 @@ +use serde::Serialize; + +use crate::{ + r_println, + subcmd::{ + errors::{CmdExecuteError, CmdPrepareError, CmdProcessError}, + renderer::{JVRenderResult, JVResultRenderer}, + }, +}; +use std::future::Future; + +pub struct JVCommandContext { + pub help: bool, + pub confirmed: bool, +} + +pub trait JVCommand<Argument, Input, Output, Renderer> +where + Argument: clap::Parser + Send + Sync, + Input: Send + Sync, + Output: Serialize + Send + Sync, + Renderer: JVResultRenderer<Output> + Send + Sync, +{ + /// Get help string for the command + fn get_help_str() -> String; + + /// performing any necessary post-execution processing + fn process( + args: Vec<String>, + ctx: JVCommandContext, + ) -> impl Future<Output = Result<JVRenderResult, CmdProcessError>> + Send + Sync + where + Self: Sync, + { + Self::process_with_renderer::<Renderer>(args, ctx) + } + + /// Process the command output with a custom renderer, + /// performing any necessary post-execution processing + fn process_with_renderer<R: JVResultRenderer<Output> + Send + Sync>( + args: Vec<String>, + ctx: JVCommandContext, + ) -> impl Future<Output = Result<JVRenderResult, CmdProcessError>> + Send + Sync + where + Self: Sync, + { + async move { + let mut full_args = vec!["jv".to_string()]; + full_args.extend(args); + let parsed_args = match Argument::try_parse_from(full_args) { + Ok(args) => args, + Err(_) => return Err(CmdProcessError::ParseError(Self::get_help_str())), + }; + // If the help flag is used, skip execution and directly print help + if ctx.help { + let mut r = JVRenderResult::default(); + r_println!(r, "{}", Self::get_help_str()); + return Ok(r); + } + let input = match Self::prepare(parsed_args, ctx).await { + Ok(input) => input, + Err(e) => return Err(CmdProcessError::from(e)), + }; + let output = match Self::exec(input).await { + Ok(output) => output, + Err(e) => return Err(CmdProcessError::from(e)), + }; + match R::render(&output).await { + Ok(r) => Ok(r), + Err(e) => Err(CmdProcessError::from(e)), + } + } + } + + /// Prepare to run the command, + /// converting Clap input into the command's supported input + fn prepare( + args: Argument, + ctx: JVCommandContext, + ) -> impl Future<Output = Result<Input, CmdPrepareError>> + Send + Sync; + + /// Run the command phase, + /// returning an output structure, waiting for rendering + fn exec(args: Input) -> impl Future<Output = Result<Output, CmdExecuteError>> + Send + Sync; +} |
