summaryrefslogtreecommitdiff
path: root/src/subcmd/cmd.rs
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-01-22 08:32:29 +0800
committer魏曹先生 <1992414357@qq.com>2026-01-22 08:32:29 +0800
commitaca8b408755f9041da9ee083c625de2a8d8c6785 (patch)
tree5747d389d5218ccf39e2153ae1346f7b5bfe8fb8 /src/subcmd/cmd.rs
parent0d614f3e2104e9b840ebc7e53a6caa6af1671636 (diff)
Refactor CLI command processing with new architecture
Diffstat (limited to 'src/subcmd/cmd.rs')
-rw-r--r--src/subcmd/cmd.rs85
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;
+}