claude_wrapper/command/
marketplace.rs1#[cfg(feature = "async")]
8use crate::Claude;
9use crate::command::ClaudeCommand;
10#[cfg(feature = "async")]
11use crate::error::Result;
12#[cfg(feature = "async")]
13use crate::exec;
14use crate::exec::CommandOutput;
15use crate::types::Scope;
16
17#[derive(Debug, Clone, Default)]
32pub struct MarketplaceListCommand {
33 json: bool,
34}
35
36impl MarketplaceListCommand {
37 #[must_use]
39 pub fn new() -> Self {
40 Self::default()
41 }
42
43 #[must_use]
45 pub fn json(mut self) -> Self {
46 self.json = true;
47 self
48 }
49}
50
51impl ClaudeCommand for MarketplaceListCommand {
52 type Output = CommandOutput;
53
54 fn args(&self) -> Vec<String> {
55 let mut args = vec![
56 "plugin".to_string(),
57 "marketplace".to_string(),
58 "list".to_string(),
59 ];
60 if self.json {
61 args.push("--json".to_string());
62 }
63 args
64 }
65
66 #[cfg(feature = "async")]
67 async fn execute(&self, claude: &Claude) -> Result<CommandOutput> {
68 exec::run_claude(claude, self.args()).await
69 }
70}
71
72#[derive(Debug, Clone)]
89pub struct MarketplaceAddCommand {
90 source: String,
91 scope: Option<Scope>,
92 sparse: Vec<String>,
93}
94
95impl MarketplaceAddCommand {
96 #[must_use]
98 pub fn new(source: impl Into<String>) -> Self {
99 Self {
100 source: source.into(),
101 scope: None,
102 sparse: Vec::new(),
103 }
104 }
105
106 #[must_use]
108 pub fn scope(mut self, scope: Scope) -> Self {
109 self.scope = Some(scope);
110 self
111 }
112
113 #[must_use]
115 pub fn sparse(mut self, paths: impl IntoIterator<Item = impl Into<String>>) -> Self {
116 self.sparse.extend(paths.into_iter().map(Into::into));
117 self
118 }
119}
120
121impl ClaudeCommand for MarketplaceAddCommand {
122 type Output = CommandOutput;
123
124 fn args(&self) -> Vec<String> {
125 let mut args = vec![
126 "plugin".to_string(),
127 "marketplace".to_string(),
128 "add".to_string(),
129 ];
130 if let Some(ref scope) = self.scope {
131 args.push("--scope".to_string());
132 args.push(scope.as_arg().to_string());
133 }
134 if !self.sparse.is_empty() {
135 args.push("--sparse".to_string());
136 args.extend(self.sparse.clone());
137 }
138 args.push(self.source.clone());
139 args
140 }
141
142 #[cfg(feature = "async")]
143 async fn execute(&self, claude: &Claude) -> Result<CommandOutput> {
144 exec::run_claude(claude, self.args()).await
145 }
146}
147
148#[derive(Debug, Clone)]
150pub struct MarketplaceRemoveCommand {
151 name: String,
152}
153
154impl MarketplaceRemoveCommand {
155 #[must_use]
157 pub fn new(name: impl Into<String>) -> Self {
158 Self { name: name.into() }
159 }
160}
161
162impl ClaudeCommand for MarketplaceRemoveCommand {
163 type Output = CommandOutput;
164
165 fn args(&self) -> Vec<String> {
166 vec![
167 "plugin".to_string(),
168 "marketplace".to_string(),
169 "remove".to_string(),
170 self.name.clone(),
171 ]
172 }
173
174 #[cfg(feature = "async")]
175 async fn execute(&self, claude: &Claude) -> Result<CommandOutput> {
176 exec::run_claude(claude, self.args()).await
177 }
178}
179
180#[derive(Debug, Clone, Default)]
182pub struct MarketplaceUpdateCommand {
183 name: Option<String>,
184}
185
186impl MarketplaceUpdateCommand {
187 #[must_use]
189 pub fn all() -> Self {
190 Self { name: None }
191 }
192
193 #[must_use]
195 pub fn new(name: impl Into<String>) -> Self {
196 Self {
197 name: Some(name.into()),
198 }
199 }
200}
201
202impl ClaudeCommand for MarketplaceUpdateCommand {
203 type Output = CommandOutput;
204
205 fn args(&self) -> Vec<String> {
206 let mut args = vec![
207 "plugin".to_string(),
208 "marketplace".to_string(),
209 "update".to_string(),
210 ];
211 if let Some(ref name) = self.name {
212 args.push(name.clone());
213 }
214 args
215 }
216
217 #[cfg(feature = "async")]
218 async fn execute(&self, claude: &Claude) -> Result<CommandOutput> {
219 exec::run_claude(claude, self.args()).await
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226 use crate::command::ClaudeCommand;
227
228 #[test]
229 fn test_marketplace_list() {
230 let cmd = MarketplaceListCommand::new().json();
231 assert_eq!(
232 ClaudeCommand::args(&cmd),
233 vec!["plugin", "marketplace", "list", "--json"]
234 );
235 }
236
237 #[test]
238 fn test_marketplace_add() {
239 let cmd = MarketplaceAddCommand::new("https://github.com/org/mp").scope(Scope::User);
240 assert_eq!(
241 ClaudeCommand::args(&cmd),
242 vec![
243 "plugin",
244 "marketplace",
245 "add",
246 "--scope",
247 "user",
248 "https://github.com/org/mp"
249 ]
250 );
251 }
252
253 #[test]
254 fn test_marketplace_add_sparse() {
255 let cmd = MarketplaceAddCommand::new("https://github.com/org/monorepo")
256 .sparse([".claude-plugin", "plugins"]);
257 let args = ClaudeCommand::args(&cmd);
258 assert!(args.contains(&"--sparse".to_string()));
259 assert!(args.contains(&".claude-plugin".to_string()));
260 assert!(args.contains(&"plugins".to_string()));
261 }
262
263 #[test]
264 fn test_marketplace_remove() {
265 let cmd = MarketplaceRemoveCommand::new("old-mp");
266 assert_eq!(
267 ClaudeCommand::args(&cmd),
268 vec!["plugin", "marketplace", "remove", "old-mp"]
269 );
270 }
271
272 #[test]
273 fn test_marketplace_update_all() {
274 let cmd = MarketplaceUpdateCommand::all();
275 assert_eq!(
276 ClaudeCommand::args(&cmd),
277 vec!["plugin", "marketplace", "update"]
278 );
279 }
280
281 #[test]
282 fn test_marketplace_update_specific() {
283 let cmd = MarketplaceUpdateCommand::new("my-mp");
284 assert_eq!(
285 ClaudeCommand::args(&cmd),
286 vec!["plugin", "marketplace", "update", "my-mp"]
287 );
288 }
289}