summaryrefslogtreecommitdiff
path: root/src/cmd/cmd_system.rs
blob: 09daf5a9318202e595f39b6f9c6a6b782fe98de6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use serde::Serialize;

use crate::{
    cmd::{
        errors::{CmdExecuteError, CmdPrepareError, CmdProcessError, CmdRenderError},
        renderer::{JVRenderResult, JVResultRenderer},
    },
    r_println,
};
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;

    /// Process the command with a specified renderer, performing any necessary post-execution processing
    fn process_with_renderer_flag(
        args: Vec<String>,
        ctx: JVCommandContext,
        renderer: String,
    ) -> impl Future<Output = Result<JVRenderResult, CmdProcessError>> + Send
    where
        Self: Sync,
    {
        async move {
            let renderer_str = renderer.as_str();
            include!("renderers/_renderers.rs")
        }
    }

    /// performing any necessary post-execution processing
    fn process(
        args: Vec<String>,
        ctx: JVCommandContext,
    ) -> impl Future<Output = Result<JVRenderResult, CmdProcessError>> + Send
    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
    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;

    /// Run the command phase,
    /// returning an output structure, waiting for rendering
    fn exec(input: Input) -> impl Future<Output = Result<Output, CmdExecuteError>> + Send;
}