Skip to main content

trustformers_core/plugins/
info.rs

1//! Plugin metadata and information structures.
2
3use crate::errors::{Result, TrustformersError};
4use semver::{Version, VersionReq};
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8/// Plugin metadata and compatibility information.
9///
10/// `PluginInfo` contains all the metadata needed to identify, validate,
11/// and load a plugin. This includes version information, dependencies,
12/// capabilities, and compatibility requirements.
13///
14/// # Example
15///
16/// ```no_run
17/// use trustformers_core::plugins::PluginInfo;
18///
19/// let info = PluginInfo::new(
20///     "custom_attention",
21///     "1.2.0",
22///     "Optimized multi-head attention with flash attention support",
23///     &["trustformers-core >= 0.1.0", "cuda >= 11.0"]
24/// );
25///
26/// assert_eq!(info.name(), "custom_attention");
27/// assert_eq!(info.version().to_string(), "1.2.0");
28/// assert!(info.is_compatible_with("trustformers-core", "0.2.0"));
29/// ```
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct PluginInfo {
32    /// Plugin name
33    name: String,
34    /// Plugin version
35    version: Version,
36    /// Human-readable description
37    description: String,
38    /// Author information
39    author: Option<String>,
40    /// Plugin homepage or repository URL
41    homepage: Option<String>,
42    /// License identifier (e.g., "MIT", "Apache-2.0")
43    license: Option<String>,
44    /// Compatibility requirements
45    dependencies: Vec<Dependency>,
46    /// Plugin capabilities
47    capabilities: Vec<String>,
48    /// Plugin tags for categorization
49    tags: Vec<String>,
50    /// Minimum system requirements
51    requirements: SystemRequirements,
52    /// Plugin entry point or library path
53    entry_point: String,
54    /// Additional metadata
55    metadata: HashMap<String, serde_json::Value>,
56}
57
58impl PluginInfo {
59    /// Creates a new plugin info instance.
60    ///
61    /// # Arguments
62    ///
63    /// * `name` - Plugin name
64    /// * `version` - Plugin version string (must be valid semver)
65    /// * `description` - Plugin description
66    /// * `dependencies` - Array of dependency specifications
67    ///
68    /// # Returns
69    ///
70    /// A new `PluginInfo` instance.
71    ///
72    /// # Panics
73    ///
74    /// Panics if the version string is not valid semver.
75    ///
76    /// # Example
77    ///
78    /// ```no_run
79    /// use trustformers_core::plugins::PluginInfo;
80    ///
81    /// let info = PluginInfo::new(
82    ///     "my_plugin",
83    ///     "1.0.0",
84    ///     "A sample plugin",
85    ///     &["trustformers-core >= 0.1.0"]
86    /// );
87    /// ```
88    pub fn new(name: &str, version: &str, description: &str, dependencies: &[&str]) -> Self {
89        let version = Version::parse(version).expect("Invalid version string");
90
91        let deps = dependencies.iter().filter_map(|dep| Dependency::parse(dep).ok()).collect();
92
93        Self {
94            name: name.to_string(),
95            version,
96            description: description.to_string(),
97            author: None,
98            homepage: None,
99            license: None,
100            dependencies: deps,
101            capabilities: Vec::new(),
102            tags: Vec::new(),
103            requirements: SystemRequirements::default(),
104            entry_point: String::new(),
105            metadata: HashMap::new(),
106        }
107    }
108
109    /// Returns the plugin name.
110    pub fn name(&self) -> &str {
111        &self.name
112    }
113
114    /// Returns the plugin version.
115    pub fn version(&self) -> &Version {
116        &self.version
117    }
118
119    /// Returns the plugin description.
120    pub fn description(&self) -> &str {
121        &self.description
122    }
123
124    /// Returns the plugin author.
125    pub fn author(&self) -> Option<&str> {
126        self.author.as_deref()
127    }
128
129    /// Sets the plugin author.
130    pub fn set_author(&mut self, author: String) {
131        self.author = Some(author);
132    }
133
134    /// Returns the plugin homepage URL.
135    pub fn homepage(&self) -> Option<&str> {
136        self.homepage.as_deref()
137    }
138
139    /// Sets the plugin homepage URL.
140    pub fn set_homepage(&mut self, homepage: String) {
141        self.homepage = Some(homepage);
142    }
143
144    /// Returns the plugin license.
145    pub fn license(&self) -> Option<&str> {
146        self.license.as_deref()
147    }
148
149    /// Sets the plugin license.
150    pub fn set_license(&mut self, license: String) {
151        self.license = Some(license);
152    }
153
154    /// Returns the plugin dependencies.
155    pub fn dependencies(&self) -> &[Dependency] {
156        &self.dependencies
157    }
158
159    /// Adds a dependency requirement.
160    pub fn add_dependency(&mut self, dependency: Dependency) {
161        self.dependencies.push(dependency);
162    }
163
164    /// Returns the plugin capabilities.
165    pub fn capabilities(&self) -> &[String] {
166        &self.capabilities
167    }
168
169    /// Adds a capability.
170    pub fn add_capability(&mut self, capability: String) {
171        self.capabilities.push(capability);
172    }
173
174    /// Returns the plugin tags.
175    pub fn tags(&self) -> &[String] {
176        &self.tags
177    }
178
179    /// Adds a tag.
180    pub fn add_tag(&mut self, tag: String) {
181        self.tags.push(tag);
182    }
183
184    /// Returns the system requirements.
185    pub fn requirements(&self) -> &SystemRequirements {
186        &self.requirements
187    }
188
189    /// Sets the system requirements.
190    pub fn set_requirements(&mut self, requirements: SystemRequirements) {
191        self.requirements = requirements;
192    }
193
194    /// Returns the plugin entry point.
195    pub fn entry_point(&self) -> &str {
196        &self.entry_point
197    }
198
199    /// Sets the plugin entry point.
200    pub fn set_entry_point(&mut self, entry_point: String) {
201        self.entry_point = entry_point;
202    }
203
204    /// Returns the plugin metadata.
205    pub fn metadata(&self) -> &HashMap<String, serde_json::Value> {
206        &self.metadata
207    }
208
209    /// Adds metadata.
210    pub fn add_metadata(&mut self, key: String, value: serde_json::Value) {
211        self.metadata.insert(key, value);
212    }
213
214    /// Checks if this plugin is compatible with a given dependency.
215    ///
216    /// # Arguments
217    ///
218    /// * `name` - The dependency name
219    /// * `version` - The dependency version
220    ///
221    /// # Returns
222    ///
223    /// `true` if compatible, `false` otherwise.
224    pub fn is_compatible_with(&self, name: &str, version: &str) -> bool {
225        if let Ok(dep_version) = Version::parse(version) {
226            for dep in &self.dependencies {
227                if dep.name == name {
228                    return dep.requirement.matches(&dep_version);
229                }
230            }
231        }
232        true // No dependency found means compatible
233    }
234
235    /// Validates the plugin info for completeness and correctness.
236    ///
237    /// # Returns
238    ///
239    /// `Ok(())` if valid, error otherwise.
240    pub fn validate(&self) -> Result<()> {
241        if self.name.is_empty() {
242            return Err(TrustformersError::invalid_config(
243                "Plugin name cannot be empty".to_string(),
244            ));
245        }
246
247        if self.description.is_empty() {
248            return Err(TrustformersError::invalid_config(
249                "Plugin description cannot be empty".to_string(),
250            ));
251        }
252
253        if self.entry_point.is_empty() {
254            return Err(TrustformersError::invalid_config(
255                "Plugin entry point cannot be empty".to_string(),
256            ));
257        }
258
259        // Validate dependencies
260        for dep in &self.dependencies {
261            dep.validate()?;
262        }
263
264        // Validate system requirements
265        self.requirements.validate()?;
266
267        Ok(())
268    }
269}
270
271/// Plugin dependency specification.
272///
273/// Represents a dependency on another plugin or system component
274/// with version requirements.
275#[derive(Debug, Clone, Serialize, Deserialize)]
276pub struct Dependency {
277    /// Dependency name
278    pub name: String,
279    /// Version requirement
280    pub requirement: VersionReq,
281    /// Whether this dependency is optional
282    pub optional: bool,
283    /// Dependency features required
284    pub features: Vec<String>,
285}
286
287impl Dependency {
288    /// Creates a new dependency.
289    ///
290    /// # Arguments
291    ///
292    /// * `name` - Dependency name
293    /// * `requirement` - Version requirement string
294    ///
295    /// # Returns
296    ///
297    /// A new dependency instance.
298    pub fn new(name: &str, requirement: &str) -> Result<Self> {
299        let req = VersionReq::parse(requirement).map_err(|e| {
300            TrustformersError::invalid_config(format!(
301                "Invalid version requirement '{}': {}",
302                requirement, e
303            ))
304        })?;
305
306        Ok(Self {
307            name: name.to_string(),
308            requirement: req,
309            optional: false,
310            features: Vec::new(),
311        })
312    }
313
314    /// Parses a dependency string.
315    ///
316    /// Supports formats like:
317    /// - "name >= 1.0.0"
318    /// - "name = 1.2.3"
319    /// - "name"
320    ///
321    /// # Arguments
322    ///
323    /// * `spec` - Dependency specification string
324    ///
325    /// # Returns
326    ///
327    /// A parsed dependency.
328    pub fn parse(spec: &str) -> Result<Self> {
329        let parts: Vec<&str> = spec.splitn(2, ' ').collect();
330        let name = parts[0].to_string();
331
332        let requirement = if parts.len() > 1 {
333            VersionReq::parse(parts[1].trim()).map_err(|e| {
334                TrustformersError::invalid_config(format!(
335                    "Invalid dependency spec '{}': {}",
336                    spec, e
337                ))
338            })?
339        } else {
340            VersionReq::STAR
341        };
342
343        Ok(Self {
344            name,
345            requirement,
346            optional: false,
347            features: Vec::new(),
348        })
349    }
350
351    /// Sets the dependency as optional.
352    pub fn optional(mut self) -> Self {
353        self.optional = true;
354        self
355    }
356
357    /// Adds required features.
358    pub fn with_features(mut self, features: Vec<String>) -> Self {
359        self.features = features;
360        self
361    }
362
363    /// Validates the dependency.
364    pub fn validate(&self) -> Result<()> {
365        if self.name.is_empty() {
366            return Err(TrustformersError::invalid_config(
367                "Dependency name cannot be empty".to_string(),
368            ));
369        }
370        Ok(())
371    }
372}
373
374/// System requirements for running a plugin.
375///
376/// Specifies minimum hardware and software requirements
377/// needed for the plugin to function correctly.
378#[derive(Debug, Clone, Serialize, Deserialize, Default)]
379pub struct SystemRequirements {
380    /// Minimum RAM in MB
381    pub min_memory_mb: Option<u64>,
382    /// Minimum free disk space in MB
383    pub min_disk_mb: Option<u64>,
384    /// Required CPU features (e.g., "avx2", "sse4.1")
385    pub cpu_features: Vec<String>,
386    /// GPU requirements
387    pub gpu: Option<GpuRequirements>,
388    /// Operating system requirements
389    pub os: Vec<String>,
390    /// Architecture requirements (e.g., "x86_64", "aarch64")
391    pub arch: Vec<String>,
392}
393
394impl SystemRequirements {
395    /// Creates new system requirements.
396    pub fn new() -> Self {
397        Self::default()
398    }
399
400    /// Sets minimum memory requirement.
401    pub fn min_memory_mb(mut self, memory_mb: u64) -> Self {
402        self.min_memory_mb = Some(memory_mb);
403        self
404    }
405
406    /// Sets minimum disk space requirement.
407    pub fn min_disk_mb(mut self, disk_mb: u64) -> Self {
408        self.min_disk_mb = Some(disk_mb);
409        self
410    }
411
412    /// Adds CPU feature requirement.
413    pub fn cpu_feature(mut self, feature: String) -> Self {
414        self.cpu_features.push(feature);
415        self
416    }
417
418    /// Sets GPU requirements.
419    pub fn gpu(mut self, gpu: GpuRequirements) -> Self {
420        self.gpu = Some(gpu);
421        self
422    }
423
424    /// Adds OS requirement.
425    pub fn os(mut self, os: String) -> Self {
426        self.os.push(os);
427        self
428    }
429
430    /// Adds architecture requirement.
431    pub fn arch(mut self, arch: String) -> Self {
432        self.arch.push(arch);
433        self
434    }
435
436    /// Validates the system requirements.
437    pub fn validate(&self) -> Result<()> {
438        if let Some(gpu) = &self.gpu {
439            gpu.validate()?;
440        }
441        Ok(())
442    }
443}
444
445/// GPU requirements specification.
446#[derive(Debug, Clone, Serialize, Deserialize)]
447pub struct GpuRequirements {
448    /// Minimum GPU memory in MB
449    pub min_memory_mb: u64,
450    /// Required compute capability (for CUDA)
451    pub compute_capability: Option<String>,
452    /// Required GPU vendors (e.g., "nvidia", "amd", "intel")
453    pub vendors: Vec<String>,
454    /// Required GPU APIs (e.g., "cuda", "opencl", "vulkan")
455    pub apis: Vec<String>,
456}
457
458impl GpuRequirements {
459    /// Creates new GPU requirements.
460    pub fn new(min_memory_mb: u64) -> Self {
461        Self {
462            min_memory_mb,
463            compute_capability: None,
464            vendors: Vec::new(),
465            apis: Vec::new(),
466        }
467    }
468
469    /// Sets compute capability requirement.
470    pub fn compute_capability(mut self, capability: String) -> Self {
471        self.compute_capability = Some(capability);
472        self
473    }
474
475    /// Adds vendor requirement.
476    pub fn vendor(mut self, vendor: String) -> Self {
477        self.vendors.push(vendor);
478        self
479    }
480
481    /// Adds API requirement.
482    pub fn api(mut self, api: String) -> Self {
483        self.apis.push(api);
484        self
485    }
486
487    /// Validates GPU requirements.
488    pub fn validate(&self) -> Result<()> {
489        if self.min_memory_mb == 0 {
490            return Err(TrustformersError::invalid_config(
491                "GPU memory requirement must be greater than 0".to_string(),
492            ));
493        }
494        Ok(())
495    }
496}