vx_plugin/plugin.rs
1//! Combined plugin trait and plugin management functionality
2//!
3//! This module defines the `VxPlugin` trait, which is the main interface for
4//! creating plugins that can provide both tools and package managers.
5
6use crate::{Result, VxPackageManager, VxTool};
7use async_trait::async_trait;
8use std::collections::HashMap;
9
10/// Combined plugin trait that can provide both tools and package managers
11///
12/// This is the main trait that plugin developers implement to register their functionality.
13/// A plugin can provide tools, package managers, or both.
14///
15/// # Example
16///
17/// ```rust,no_run
18/// use vx_plugin::{VxPlugin, VxTool, VxPackageManager, Result};
19/// use async_trait::async_trait;
20///
21/// struct MyPlugin;
22///
23/// #[async_trait]
24/// impl VxPlugin for MyPlugin {
25/// fn name(&self) -> &str {
26/// "my-plugin"
27/// }
28///
29/// fn description(&self) -> &str {
30/// "A plugin that provides custom tools and package managers"
31/// }
32///
33/// fn tools(&self) -> Vec<Box<dyn VxTool>> {
34/// // Return your tool implementations
35/// vec![]
36/// }
37///
38/// fn package_managers(&self) -> Vec<Box<dyn VxPackageManager>> {
39/// // Return your package manager implementations
40/// vec![]
41/// }
42/// }
43/// ```
44#[async_trait]
45pub trait VxPlugin: Send + Sync {
46 /// Plugin name (required)
47 ///
48 /// This should be a unique identifier for the plugin.
49 fn name(&self) -> &str;
50
51 /// Plugin description (optional)
52 ///
53 /// A human-readable description of what this plugin provides.
54 fn description(&self) -> &str {
55 "A vx plugin"
56 }
57
58 /// Plugin version (optional)
59 ///
60 /// The version of this plugin implementation.
61 fn version(&self) -> &str {
62 "0.1.0"
63 }
64
65 /// Plugin author (optional)
66 ///
67 /// Information about who created this plugin.
68 fn author(&self) -> Option<&str> {
69 None
70 }
71
72 /// Plugin homepage or repository URL (optional)
73 ///
74 /// URL where users can find more information about this plugin.
75 fn homepage(&self) -> Option<&str> {
76 None
77 }
78
79 /// Get all tools provided by this plugin
80 ///
81 /// Return a vector of tool implementations that this plugin provides.
82 /// Return an empty vector if this plugin doesn't provide any tools.
83 fn tools(&self) -> Vec<Box<dyn VxTool>> {
84 vec![]
85 }
86
87 /// Get all package managers provided by this plugin
88 ///
89 /// Return a vector of package manager implementations that this plugin provides.
90 /// Return an empty vector if this plugin doesn't provide any package managers.
91 fn package_managers(&self) -> Vec<Box<dyn VxPackageManager>> {
92 vec![]
93 }
94 /// Initialize the plugin (optional)
95 ///
96 /// This method is called when the plugin is loaded. Use it to perform
97 /// any necessary setup, such as checking dependencies or initializing
98 /// internal state.
99 async fn initialize(&mut self) -> Result<()> {
100 Ok(())
101 }
102
103 /// Shutdown the plugin (optional)
104 ///
105 /// This method is called when the plugin is being unloaded. Use it to
106 /// perform cleanup operations.
107 async fn shutdown(&mut self) -> Result<()> {
108 Ok(())
109 }
110
111 /// Check if this plugin supports a specific tool
112 ///
113 /// Default implementation checks all tools provided by this plugin,
114 /// including their aliases.
115 fn supports_tool(&self, tool_name: &str) -> bool {
116 self.tools()
117 .iter()
118 .any(|tool| tool.name() == tool_name || tool.aliases().contains(&tool_name))
119 }
120
121 /// Check if this plugin supports a specific package manager
122 ///
123 /// Default implementation checks all package managers provided by this plugin.
124 fn supports_package_manager(&self, pm_name: &str) -> bool {
125 self.package_managers()
126 .iter()
127 .any(|pm| pm.name() == pm_name)
128 }
129
130 /// Get a specific tool by name
131 ///
132 /// Returns the first tool that matches the given name or alias.
133 /// Note: This method returns an owned Box since the tools are owned by the plugin.
134 fn get_tool(&self, tool_name: &str) -> Option<Box<dyn VxTool>> {
135 self.tools()
136 .into_iter()
137 .find(|tool| tool.name() == tool_name || tool.aliases().contains(&tool_name))
138 }
139
140 /// Get a specific package manager by name
141 ///
142 /// Returns the first package manager that matches the given name.
143 /// Note: This method returns an owned Box since the package managers are owned by the plugin.
144 fn get_package_manager(&self, pm_name: &str) -> Option<Box<dyn VxPackageManager>> {
145 self.package_managers()
146 .into_iter()
147 .find(|pm| pm.name() == pm_name)
148 }
149 /// Check plugin compatibility with the current vx version
150 ///
151 /// Override this to implement version compatibility checks.
152 /// The default implementation accepts all versions.
153 fn is_compatible_with(&self, vx_version: &str) -> bool {
154 let _ = vx_version;
155 true
156 }
157
158 /// Get plugin dependencies
159 ///
160 /// Return a list of other plugins that this plugin depends on.
161 /// The default implementation has no dependencies.
162 fn dependencies(&self) -> Vec<&str> {
163 vec![]
164 }
165
166 /// Get plugin configuration schema
167 ///
168 /// Return a JSON schema describing the configuration options
169 /// that this plugin accepts. The default implementation has no configuration.
170 fn config_schema(&self) -> Option<serde_json::Value> {
171 None
172 }
173
174 /// Validate plugin configuration
175 ///
176 /// Check if the provided configuration is valid for this plugin.
177 /// The default implementation accepts any configuration.
178 fn validate_config(&self, _config: &serde_json::Value) -> Result<()> {
179 Ok(())
180 }
181
182 /// Additional metadata for the plugin (optional)
183 ///
184 /// Override this to provide plugin-specific metadata such as
185 /// supported platforms, feature flags, etc.
186 fn metadata(&self) -> HashMap<String, String> {
187 let mut metadata = HashMap::new();
188 metadata.insert("name".to_string(), self.name().to_string());
189 metadata.insert("version".to_string(), self.version().to_string());
190 metadata.insert("description".to_string(), self.description().to_string());
191
192 if let Some(author) = self.author() {
193 metadata.insert("author".to_string(), author.to_string());
194 }
195
196 if let Some(homepage) = self.homepage() {
197 metadata.insert("homepage".to_string(), homepage.to_string());
198 }
199
200 metadata
201 }
202}
203
204/// Standard plugin implementation for single-tool plugins
205///
206/// This is a convenience implementation for plugins that provide a single tool.
207/// It handles the boilerplate of implementing VxPlugin for simple cases.
208pub struct StandardPlugin {
209 name: String,
210 description: String,
211 version: String,
212 author: Option<String>,
213 homepage: Option<String>,
214 tool_factory: Box<dyn Fn() -> Box<dyn VxTool> + Send + Sync>,
215}
216
217impl StandardPlugin {
218 /// Create a new standard plugin
219 ///
220 /// # Arguments
221 ///
222 /// * `name` - Plugin name
223 /// * `description` - Plugin description
224 /// * `version` - Plugin version
225 /// * `tool_factory` - Factory function that creates the tool instance
226 pub fn new<F>(
227 name: impl Into<String>,
228 description: impl Into<String>,
229 version: impl Into<String>,
230 tool_factory: F,
231 ) -> Self
232 where
233 F: Fn() -> Box<dyn VxTool> + Send + Sync + 'static,
234 {
235 Self {
236 name: name.into(),
237 description: description.into(),
238 version: version.into(),
239 author: None,
240 homepage: None,
241 tool_factory: Box::new(tool_factory),
242 }
243 }
244
245 /// Set the plugin author
246 pub fn with_author(mut self, author: impl Into<String>) -> Self {
247 self.author = Some(author.into());
248 self
249 }
250
251 /// Set the plugin homepage
252 pub fn with_homepage(mut self, homepage: impl Into<String>) -> Self {
253 self.homepage = Some(homepage.into());
254 self
255 }
256}
257
258#[async_trait]
259impl VxPlugin for StandardPlugin {
260 fn name(&self) -> &str {
261 &self.name
262 }
263
264 fn description(&self) -> &str {
265 &self.description
266 }
267
268 fn version(&self) -> &str {
269 &self.version
270 }
271
272 fn author(&self) -> Option<&str> {
273 self.author.as_deref()
274 }
275
276 fn homepage(&self) -> Option<&str> {
277 self.homepage.as_deref()
278 }
279
280 fn tools(&self) -> Vec<Box<dyn VxTool>> {
281 vec![(self.tool_factory)()]
282 }
283}