Skip to main content

smooth_plugin/
command.rs

1use std::future::Future;
2use std::pin::Pin;
3
4/// Type alias for the async handler function used by plugin commands.
5pub type CommandHandler = Box<dyn Fn(Vec<String>) -> Pin<Box<dyn Future<Output = anyhow::Result<()>> + Send>> + Send + Sync>;
6
7/// A CLI command provided by a plugin.
8///
9/// Commands have a name, description, optional subcommands, and an optional
10/// async handler function. Use [`PluginCommandBuilder`] for ergonomic construction.
11pub struct PluginCommand {
12    pub name: String,
13    pub description: String,
14    pub subcommands: Vec<Self>,
15    pub handler: Option<CommandHandler>,
16}
17
18impl std::fmt::Debug for PluginCommand {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        f.debug_struct("PluginCommand")
21            .field("name", &self.name)
22            .field("description", &self.description)
23            .field("subcommands", &self.subcommands)
24            .field("handler", &self.handler.as_ref().map(|_| "<fn>"))
25            .finish()
26    }
27}
28
29/// Builder for constructing [`PluginCommand`] instances.
30pub struct PluginCommandBuilder {
31    name: String,
32    description: String,
33    subcommands: Vec<PluginCommand>,
34    handler: Option<CommandHandler>,
35}
36
37impl PluginCommandBuilder {
38    /// Create a new builder with the given command name.
39    pub fn new(name: impl Into<String>) -> Self {
40        Self {
41            name: name.into(),
42            description: String::new(),
43            subcommands: vec![],
44            handler: None,
45        }
46    }
47
48    /// Set the command description.
49    #[must_use]
50    pub fn description(mut self, desc: impl Into<String>) -> Self {
51        self.description = desc.into();
52        self
53    }
54
55    /// Add a subcommand.
56    #[must_use]
57    pub fn subcommand(mut self, cmd: PluginCommand) -> Self {
58        self.subcommands.push(cmd);
59        self
60    }
61
62    /// Set the async handler function.
63    #[must_use]
64    pub fn handler<F, Fut>(mut self, f: F) -> Self
65    where
66        F: Fn(Vec<String>) -> Fut + Send + Sync + 'static,
67        Fut: Future<Output = anyhow::Result<()>> + Send + 'static,
68    {
69        self.handler = Some(Box::new(move |args| Box::pin(f(args))));
70        self
71    }
72
73    /// Build the `PluginCommand`.
74    pub fn build(self) -> PluginCommand {
75        PluginCommand {
76            name: self.name,
77            description: self.description,
78            subcommands: self.subcommands,
79            handler: self.handler,
80        }
81    }
82}