Skip to main content

ai_agent/commands/
plugin_parse_args.rs

1// Parse plugin subcommand arguments into structured commands
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6#[serde(tag = "type")]
7pub enum ParsedCommand {
8    #[serde(rename = "menu")]
9    Menu,
10    #[serde(rename = "help")]
11    Help,
12    #[serde(rename = "install")]
13    Install {
14        #[serde(skip_serializing_if = "Option::is_none")]
15        marketplace: Option<String>,
16        #[serde(skip_serializing_if = "Option::is_none")]
17        plugin: Option<String>,
18    },
19    #[serde(rename = "manage")]
20    Manage,
21    #[serde(rename = "uninstall")]
22    Uninstall {
23        #[serde(skip_serializing_if = "Option::is_none")]
24        plugin: Option<String>,
25    },
26    #[serde(rename = "enable")]
27    Enable {
28        #[serde(skip_serializing_if = "Option::is_none")]
29        plugin: Option<String>,
30    },
31    #[serde(rename = "disable")]
32    Disable {
33        #[serde(skip_serializing_if = "Option::is_none")]
34        plugin: Option<String>,
35    },
36    #[serde(rename = "validate")]
37    Validate {
38        #[serde(skip_serializing_if = "Option::is_none")]
39        path: Option<String>,
40    },
41    #[serde(rename = "marketplace")]
42    Marketplace {
43        #[serde(skip_serializing_if = "Option::is_none")]
44        action: Option<String>,
45        #[serde(skip_serializing_if = "Option::is_none")]
46        target: Option<String>,
47    },
48}
49
50pub fn parse_plugin_args(args: Option<&str>) -> ParsedCommand {
51    let args = match args {
52        Some(a) => a.trim(),
53        None => "",
54    };
55    if args.is_empty() {
56        return ParsedCommand::Menu;
57    }
58
59    let parts: Vec<&str> = args.split_whitespace().collect();
60    let command = parts.first().map(|s| s.to_lowercase());
61
62    match command.as_deref() {
63        Some("help") | Some("--help") | Some("-h") => ParsedCommand::Help,
64
65        Some("install") | Some("i") => {
66            let target = parts.get(1).map(|s| s.to_string());
67
68            if let Some(t) = target {
69                // Check if it's in format plugin@marketplace
70                if t.contains('@') {
71                    let parts: Vec<&str> = t.split('@').collect();
72                    if parts.len() == 2 {
73                        return ParsedCommand::Install {
74                            plugin: Some(parts[0].to_string()),
75                            marketplace: Some(parts[1].to_string()),
76                        };
77                    }
78                }
79
80                // Check if the target looks like a marketplace (URL or path)
81                let is_marketplace = t.starts_with("http://")
82                    || t.starts_with("https://")
83                    || t.starts_with("file://")
84                    || t.contains('/')
85                    || t.contains('\\');
86
87                if is_marketplace {
88                    return ParsedCommand::Install {
89                        marketplace: Some(t),
90                        plugin: None,
91                    };
92                }
93
94                // Otherwise treat it as a plugin name
95                return ParsedCommand::Install {
96                    plugin: Some(t),
97                    marketplace: None,
98                };
99            }
100
101            ParsedCommand::Install {
102                marketplace: None,
103                plugin: None,
104            }
105        }
106
107        Some("manage") => ParsedCommand::Manage,
108
109        Some("uninstall") => ParsedCommand::Uninstall {
110            plugin: parts.get(1).map(|s| s.to_string()),
111        },
112
113        Some("enable") => ParsedCommand::Enable {
114            plugin: parts.get(1).map(|s| s.to_string()),
115        },
116
117        Some("disable") => ParsedCommand::Disable {
118            plugin: parts.get(1).map(|s| s.to_string()),
119        },
120
121        Some("validate") => {
122            let target = parts[1..].join(" ").trim().to_string();
123            ParsedCommand::Validate {
124                path: if target.is_empty() {
125                    None
126                } else {
127                    Some(target)
128                },
129            }
130        }
131
132        Some("marketplace") | Some("market") => {
133            let action = parts.get(1).map(|s| s.to_lowercase());
134            let target = parts[2..].join(" ");
135
136            match action.as_deref() {
137                Some("add") => ParsedCommand::Marketplace {
138                    action: Some("add".to_string()),
139                    target: if target.is_empty() {
140                        None
141                    } else {
142                        Some(target)
143                    },
144                },
145                Some("remove") | Some("rm") => ParsedCommand::Marketplace {
146                    action: Some("remove".to_string()),
147                    target: if target.is_empty() {
148                        None
149                    } else {
150                        Some(target)
151                    },
152                },
153                Some("update") => ParsedCommand::Marketplace {
154                    action: Some("update".to_string()),
155                    target: if target.is_empty() {
156                        None
157                    } else {
158                        Some(target)
159                    },
160                },
161                Some("list") => ParsedCommand::Marketplace {
162                    action: Some("list".to_string()),
163                    target: None,
164                },
165                _ => ParsedCommand::Marketplace {
166                    action: None,
167                    target: None,
168                },
169            }
170        }
171
172        // Unknown command, show menu
173        _ => ParsedCommand::Menu,
174    }
175}