Skip to main content

claude_wrapper/command/
raw.rs

1//! The raw-subcommand escape hatch.
2//!
3//! [`RawCommand`] runs an arbitrary `claude` subcommand with
4//! caller-supplied args, for surfaces this crate does not yet wrap as a
5//! typed builder. Output comes back as [`CommandOutput`]; there is no
6//! typed parsing.
7
8#[cfg(feature = "async")]
9use crate::Claude;
10use crate::command::ClaudeCommand;
11#[cfg(feature = "async")]
12use crate::error::Result;
13#[cfg(feature = "async")]
14use crate::exec;
15use crate::exec::CommandOutput;
16
17/// Escape hatch for running arbitrary claude subcommands not yet
18/// covered by dedicated command builders.
19///
20/// # Example
21///
22/// ```no_run
23/// use claude_wrapper::{Claude, ClaudeCommand, RawCommand};
24///
25/// # async fn example() -> claude_wrapper::Result<()> {
26/// let claude = Claude::builder().build()?;
27///
28/// let output = RawCommand::new("agents")
29///     .arg("--setting-sources")
30///     .arg("user,project")
31///     .execute(&claude)
32///     .await?;
33///
34/// println!("{}", output.stdout);
35/// # Ok(())
36/// # }
37/// ```
38#[derive(Debug, Clone)]
39pub struct RawCommand {
40    command_args: Vec<String>,
41}
42
43impl RawCommand {
44    /// Create a new raw command with the given subcommand.
45    #[must_use]
46    pub fn new(subcommand: impl Into<String>) -> Self {
47        Self {
48            command_args: vec![subcommand.into()],
49        }
50    }
51
52    /// Add a single argument.
53    #[must_use]
54    pub fn arg(mut self, arg: impl Into<String>) -> Self {
55        self.command_args.push(arg.into());
56        self
57    }
58
59    /// Add multiple arguments.
60    #[must_use]
61    pub fn args(mut self, args: impl IntoIterator<Item = impl Into<String>>) -> Self {
62        self.command_args.extend(args.into_iter().map(Into::into));
63        self
64    }
65}
66
67impl ClaudeCommand for RawCommand {
68    type Output = CommandOutput;
69
70    fn args(&self) -> Vec<String> {
71        self.command_args.clone()
72    }
73
74    #[cfg(feature = "async")]
75    async fn execute(&self, claude: &Claude) -> Result<CommandOutput> {
76        exec::run_claude(claude, self.args()).await
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83    use crate::command::ClaudeCommand;
84
85    #[test]
86    fn test_raw_command_args() {
87        let cmd = RawCommand::new("agents")
88            .arg("--setting-sources")
89            .arg("user,project");
90
91        assert_eq!(
92            ClaudeCommand::args(&cmd),
93            vec!["agents", "--setting-sources", "user,project"]
94        );
95    }
96
97    #[test]
98    fn test_raw_command_with_args() {
99        let cmd = RawCommand::new("plugin").args(["list", "--scope", "user"]);
100
101        assert_eq!(
102            ClaudeCommand::args(&cmd),
103            vec!["plugin", "list", "--scope", "user"]
104        );
105    }
106}