vanguard_plugin/
lib.rs

1#![deny(missing_docs)]
2#![deny(clippy::all)]
3
4//! Vanguard Plugin System
5//!
6//! This crate provides the core plugin interface and validation system for Vanguard.
7//! It is completely decoupled from the CLI and provides a standardized way to create
8//! and validate plugins.
9
10use async_trait::async_trait;
11use semver::Version;
12use serde::{Deserialize, Serialize};
13use std::fmt::Debug;
14use thiserror::Error;
15
16mod config;
17mod dylib;
18/// Module containing plugin export helpers and macros for implementing plugins
19pub mod exports;
20mod loader;
21mod registry;
22mod validator;
23
24pub use config::*;
25pub use dylib::{get_dylib_name, get_dylib_path, DylibError, PluginLibrary};
26pub use exports::{CreatePluginFn, CREATE_PLUGIN_SYMBOL};
27pub use loader::{LoaderConfig, LoaderError, PluginLoader};
28pub use registry::{PluginRegistry, PluginState, RegistryError};
29pub use validator::*;
30
31/// Plugin information used for storing in the registry
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct PluginInfo {
34    /// Name of the plugin
35    pub name: String,
36    /// Version of the plugin
37    pub version: String,
38    /// Description of the plugin
39    pub description: String,
40    /// Whether the plugin is enabled
41    pub enabled: bool,
42    /// Path to the plugin file
43    pub path: String,
44    /// When the plugin was installed
45    pub install_date: String,
46}
47
48/// Result of plugin validation
49#[derive(Debug)]
50pub enum ValidationResult {
51    /// Validation passed
52    Passed,
53    /// Validation failed with reason
54    Failed(String),
55}
56
57/// Represents a platform that a plugin supports
58#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
59pub struct Platform {
60    /// Operating system (e.g., "linux", "macos", "windows")
61    pub os: String,
62    /// CPU architecture (e.g., "x86_64", "aarch64")
63    pub arch: String,
64}
65
66/// Metadata for a Vanguard plugin
67#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct PluginMetadata {
69    /// Name of the plugin
70    pub name: String,
71    /// Version of the plugin
72    pub version: String,
73    /// Description of the plugin
74    pub description: String,
75    /// Author of the plugin
76    pub author: String,
77    /// Minimum compatible Vanguard version
78    pub min_vanguard_version: Option<String>,
79    /// Maximum compatible Vanguard version
80    pub max_vanguard_version: Option<String>,
81    /// Plugin dependencies
82    pub dependencies: Vec<PluginDependency>,
83}
84
85/// A dependency on another plugin
86#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct PluginDependency {
88    /// Name of the required plugin
89    pub name: String,
90    /// Version requirement for the plugin
91    pub version: String,
92}
93
94/// Errors that can occur during plugin operations
95#[derive(Error, Debug)]
96pub enum PluginError {
97    /// Plugin validation failed
98    #[error("Plugin validation failed: {0}")]
99    ValidationFailed(String),
100
101    /// Plugin initialization failed
102    #[error("Plugin initialization failed: {0}")]
103    InitializationFailed(String),
104
105    /// Plugin operation failed
106    #[error("Plugin operation failed: {0}")]
107    OperationFailed(String),
108
109    /// Plugin is incompatible
110    #[error("Plugin is incompatible: {0}")]
111    Incompatible(String),
112}
113
114/// Trait that must be implemented by all Vanguard plugins
115#[async_trait]
116pub trait VanguardPlugin: Send + Sync + std::fmt::Debug {
117    /// Get the plugin's metadata
118    fn metadata(&self) -> &PluginMetadata;
119
120    /// Validate the plugin's configuration and environment
121    async fn validate(&self) -> ValidationResult;
122
123    /// Initialize the plugin
124    async fn initialize(&self) -> Result<(), String>;
125
126    /// Clean up plugin resources
127    async fn cleanup(&self) -> Result<(), String>;
128
129    /// Check if plugin is compatible with given Vanguard version
130    fn is_compatible_with(&self, vanguard_version: &Version) -> bool {
131        let metadata = self.metadata();
132
133        // Check minimum version (inclusive)
134        if let Some(min_version) = &metadata.min_vanguard_version {
135            if vanguard_version < &Version::parse(min_version).unwrap() {
136                return false;
137            }
138        }
139
140        // Check maximum version if specified (exclusive)
141        if let Some(max_version) = &metadata.max_vanguard_version {
142            if vanguard_version >= &Version::parse(max_version).unwrap() {
143                return false;
144            }
145        }
146
147        true
148    }
149
150    /// Get plugin configuration schema
151    fn config_schema(&self) -> Option<serde_json::Value> {
152        None
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159
160    struct TestPlugin {
161        metadata: PluginMetadata,
162    }
163
164    impl std::fmt::Debug for TestPlugin {
165        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166            f.debug_struct("TestPlugin")
167                .field("metadata", &self.metadata)
168                .finish()
169        }
170    }
171
172    #[async_trait]
173    impl VanguardPlugin for TestPlugin {
174        fn metadata(&self) -> &PluginMetadata {
175            &self.metadata
176        }
177
178        async fn validate(&self) -> ValidationResult {
179            ValidationResult::Passed
180        }
181
182        async fn initialize(&self) -> Result<(), String> {
183            Ok(())
184        }
185
186        async fn cleanup(&self) -> Result<(), String> {
187            Ok(())
188        }
189    }
190
191    fn create_test_plugin() -> TestPlugin {
192        TestPlugin {
193            metadata: PluginMetadata {
194                name: "test-plugin".to_string(),
195                version: "1.0.0".to_string(),
196                description: "Test Plugin".to_string(),
197                author: "Test Author".to_string(),
198                min_vanguard_version: Some("0.1.0".to_string()),
199                max_vanguard_version: Some("2.0.0".to_string()),
200                dependencies: vec![PluginDependency {
201                    name: "core".to_string(),
202                    version: ">=1.0.0".to_string(),
203                }],
204            },
205        }
206    }
207
208    #[test]
209    fn test_plugin_metadata() {
210        let plugin = create_test_plugin();
211        assert_eq!(plugin.metadata().name, "test-plugin");
212        assert_eq!(plugin.metadata().version, "1.0.0");
213    }
214
215    #[test]
216    fn test_plugin_compatibility() {
217        let plugin = create_test_plugin();
218
219        // Test version compatibility
220        assert!(plugin.is_compatible_with(&Version::new(0, 1, 0))); // Min version
221        assert!(plugin.is_compatible_with(&Version::new(1, 0, 0))); // Compatible version
222        assert!(!plugin.is_compatible_with(&Version::new(2, 0, 0))); // Max version (exclusive)
223        assert!(!plugin.is_compatible_with(&Version::new(0, 0, 9))); // Too old
224    }
225
226    #[tokio::test]
227    async fn test_plugin_lifecycle() {
228        let plugin = create_test_plugin();
229
230        // Test initialization
231        assert!(plugin.initialize().await.is_ok());
232
233        // Test validation
234        assert!(matches!(plugin.validate().await, ValidationResult::Passed));
235
236        // Test cleanup
237        assert!(plugin.cleanup().await.is_ok());
238    }
239}