xacli_core/command/
cmd.rs

1//! Command definition
2
3use std::fmt::Debug;
4
5use crate::{Arg, Context, Result};
6
7#[derive(Debug, Clone, PartialEq)]
8pub struct CommandInfo {
9    pub name: String,
10    pub aliases: Vec<String>,
11    pub title: String,
12    pub description: String,
13}
14
15/// Command execution handler function type
16pub type RunFn = dyn Fn(&mut dyn Context) -> Result<()>;
17
18pub struct Command {
19    pub info: CommandInfo,
20    pub args: Vec<Arg>,
21    pub subcommands: Vec<Command>,
22    pub hooks: Hooks,
23}
24
25pub struct Hooks {
26    pub run_fn: Option<Box<RunFn>>,
27    pub pre_run_fn: Option<Box<RunFn>>,
28    pub post_run_fn: Option<Box<RunFn>>,
29}
30
31impl Command {
32    /// Create a new command with the given name
33    pub fn new(name: impl Into<String>) -> Self {
34        Self {
35            info: CommandInfo {
36                name: name.into(),
37                aliases: Vec::new(),
38                title: String::new(),
39                description: String::new(),
40            },
41            args: Vec::new(),
42            subcommands: Vec::new(),
43            hooks: Hooks {
44                run_fn: None,
45                pre_run_fn: None,
46                post_run_fn: None,
47            },
48        }
49    }
50
51    // ========================================================================
52    // Chainable configuration methods
53    // ========================================================================
54
55    pub fn alias(mut self, alias: impl Into<String>) -> Self {
56        self.info.aliases.push(alias.into());
57        self
58    }
59
60    pub fn aliases(mut self, aliases: Vec<String>) -> Self {
61        self.info.aliases = aliases;
62        self
63    }
64
65    pub fn title(mut self, title: impl Into<String>) -> Self {
66        self.info.title = title.into();
67        self
68    }
69
70    pub fn description(mut self, description: impl Into<String>) -> Self {
71        self.info.description = description.into();
72        self
73    }
74
75    /// Add an argument (positional, flag, or option)
76    pub fn arg(mut self, arg: Arg) -> Self {
77        self.args.push(arg);
78        self
79    }
80
81    pub fn subcommand(mut self, command: Command) -> Self {
82        self.subcommands.push(command);
83        self
84    }
85
86    pub fn run(mut self, f: Box<RunFn>) -> Self {
87        self.hooks.run_fn = Some(f);
88        self
89    }
90
91    pub fn pre_run(mut self, f: Box<RunFn>) -> Self {
92        self.hooks.pre_run_fn = Some(f);
93        self
94    }
95
96    pub fn post_run(mut self, f: Box<RunFn>) -> Self {
97        self.hooks.post_run_fn = Some(f);
98        self
99    }
100
101    // ========================================================================
102    // Execution methods
103    // ========================================================================
104
105    /// Check if this is a leaf command (no subcommands)
106    pub fn is_leaf(&self) -> bool {
107        self.subcommands.is_empty()
108    }
109
110    /// Execute the command with the given context
111    ///
112    /// Runs hooks in order: pre_run -> run -> post_run
113    pub fn execute(&self, ctx: &mut dyn Context) -> Result<()> {
114        // Run pre_run hook if set
115        if let Some(ref pre_run) = self.hooks.pre_run_fn {
116            pre_run(ctx)?;
117        }
118
119        // Run main handler if set
120        if let Some(ref run_fn) = self.hooks.run_fn {
121            run_fn(ctx)?;
122        }
123
124        // Run post_run hook if set
125        if let Some(ref post_run) = self.hooks.post_run_fn {
126            post_run(ctx)?;
127        }
128
129        Ok(())
130    }
131}