Skip to main content

null_e/plugins/
mod.rs

1//! Plugin system for DevSweep
2//!
3//! Each plugin handles detection and artifact discovery for a specific
4//! language/framework ecosystem.
5
6mod registry;
7mod node;
8mod rust;
9mod python;
10mod go;
11mod java;
12mod dotnet;
13mod swift;
14
15pub use registry::*;
16pub use node::NodePlugin;
17pub use rust::RustPlugin;
18pub use python::PythonPlugin;
19pub use go::GoPlugin;
20pub use java::{MavenPlugin, GradlePlugin};
21pub use dotnet::DotNetPlugin;
22pub use swift::SwiftPlugin;
23
24use crate::core::{Artifact, ProjectKind, ProjectMarker};
25use crate::error::Result;
26use std::path::Path;
27
28/// Trait that all language/framework plugins must implement
29pub trait Plugin: Send + Sync {
30    /// Unique identifier for this plugin
31    fn id(&self) -> &'static str;
32
33    /// Human-readable name
34    fn name(&self) -> &'static str;
35
36    /// Project kinds this plugin handles
37    fn supported_kinds(&self) -> &[ProjectKind];
38
39    /// Markers that identify projects this plugin handles
40    fn markers(&self) -> Vec<ProjectMarker>;
41
42    /// Detect if path is a project root for this plugin
43    fn detect(&self, path: &Path) -> Option<ProjectKind>;
44
45    /// Find cleanable artifacts in a project directory
46    fn find_artifacts(&self, project_root: &Path) -> Result<Vec<Artifact>>;
47
48    /// Custom size calculation (override for special cases)
49    fn calculate_size(&self, artifact: &Artifact) -> Result<u64> {
50        default_calculate_size(&artifact.path)
51    }
52
53    /// Pre-clean hook (e.g., stop running processes)
54    fn pre_clean(&self, _artifact: &Artifact) -> Result<()> {
55        Ok(())
56    }
57
58    /// Post-clean hook (e.g., update state files)
59    fn post_clean(&self, _artifact: &Artifact) -> Result<()> {
60        Ok(())
61    }
62
63    /// Priority when multiple plugins match (higher = preferred)
64    fn priority(&self) -> u8 {
65        50
66    }
67
68    /// Get cleanable directory names for fast scanning
69    fn cleanable_dirs(&self) -> &[&'static str] {
70        &[]
71    }
72}
73
74/// Calculate directory size using parallel walk
75pub fn default_calculate_size(path: &Path) -> Result<u64> {
76    use rayon::prelude::*;
77    use walkdir::WalkDir;
78
79    if !path.exists() {
80        return Ok(0);
81    }
82
83    // For small directories, use simple walk
84    let entries: Vec<_> = WalkDir::new(path)
85        .into_iter()
86        .filter_map(|e| e.ok())
87        .collect();
88
89    let size: u64 = entries
90        .par_iter()
91        .filter_map(|entry| entry.metadata().ok())
92        .filter(|m| m.is_file())
93        .map(|m| m.len())
94        .sum();
95
96    Ok(size)
97}
98
99/// Count files in a directory
100pub fn count_files(path: &Path) -> Result<u64> {
101    use walkdir::WalkDir;
102
103    if !path.exists() {
104        return Ok(0);
105    }
106
107    let count = WalkDir::new(path)
108        .into_iter()
109        .filter_map(|e| e.ok())
110        .filter(|e| e.file_type().is_file())
111        .count() as u64;
112
113    Ok(count)
114}
115
116/// Get all built-in plugins
117pub fn builtin_plugins() -> Vec<Box<dyn Plugin>> {
118    vec![
119        Box::new(NodePlugin),
120        Box::new(RustPlugin),
121        Box::new(PythonPlugin),
122        Box::new(GoPlugin),
123        Box::new(MavenPlugin),
124        Box::new(GradlePlugin),
125        Box::new(DotNetPlugin),
126        Box::new(SwiftPlugin),
127    ]
128}