Skip to main content

dev_sweep/scanner/
project.rs

1use std::fmt;
2use std::path::PathBuf;
3
4use chrono::{DateTime, Local};
5use serde::{Deserialize, Serialize};
6
7/// The kind of development project detected.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9pub enum ProjectKind {
10    Rust,
11    Node,
12    Python,
13    Java,
14    DotNet,
15    Go,
16    Zig,
17    CMake,
18    Swift,
19    Elixir,
20    Haskell,
21    Dart,
22    Ruby,
23    Scala,
24    Unity,
25    Godot,
26    Terraform,
27}
28
29impl ProjectKind {
30    /// Returns the marker file(s) used to detect this project kind.
31    pub fn marker_files(&self) -> &[&str] {
32        match self {
33            Self::Rust => &["Cargo.toml"],
34            Self::Node => &["package.json"],
35            Self::Python => &["pyproject.toml", "setup.py", "requirements.txt"],
36            Self::Java => &["pom.xml", "build.gradle", "build.gradle.kts"],
37            Self::DotNet => &["*.csproj", "*.fsproj", "*.sln"],
38            Self::Go => &["go.mod"],
39            Self::Zig => &["build.zig"],
40            Self::CMake => &["CMakeLists.txt"],
41            Self::Swift => &["Package.swift"],
42            Self::Elixir => &["mix.exs"],
43            Self::Haskell => &["stack.yaml", "*.cabal"],
44            Self::Dart => &["pubspec.yaml"],
45            Self::Ruby => &["Gemfile"],
46            Self::Scala => &["build.sbt"],
47            Self::Unity => &["ProjectSettings/ProjectVersion.txt"],
48            Self::Godot => &["project.godot"],
49            Self::Terraform => &["main.tf", "*.tf"],
50        }
51    }
52
53    /// Returns the directories that can be safely cleaned for this project kind.
54    pub fn cleanable_dirs(&self) -> &[&str] {
55        match self {
56            Self::Rust => &["target"],
57            Self::Node => &["node_modules", ".next", ".nuxt", "dist", ".cache"],
58            Self::Python => &["__pycache__", ".venv", "venv", ".tox", "*.egg-info", ".mypy_cache", ".pytest_cache"],
59            Self::Java => &["target", "build", ".gradle"],
60            Self::DotNet => &["bin", "obj"],
61            Self::Go => &[],  // Go modules are shared, not per-project artifacts
62            Self::Zig => &["zig-cache", "zig-out"],
63            Self::CMake => &["build", "cmake-build-debug", "cmake-build-release"],
64            Self::Swift => &[".build"],
65            Self::Elixir => &["_build", "deps"],
66            Self::Haskell => &[".stack-work"],
67            Self::Dart => &[".dart_tool", "build"],
68            Self::Ruby => &["vendor/bundle"],
69            Self::Scala => &["target", "project/target"],
70            Self::Unity => &["Library", "Temp", "Obj", "Logs"],
71            Self::Godot => &[".godot"],
72            Self::Terraform => &[".terraform"],
73        }
74    }
75
76    /// Returns all known project kinds.
77    pub fn all() -> &'static [ProjectKind] {
78        &[
79            Self::Rust,
80            Self::Node,
81            Self::Python,
82            Self::Java,
83            Self::DotNet,
84            Self::Go,
85            Self::Zig,
86            Self::CMake,
87            Self::Swift,
88            Self::Elixir,
89            Self::Haskell,
90            Self::Dart,
91            Self::Ruby,
92            Self::Scala,
93            Self::Unity,
94            Self::Godot,
95            Self::Terraform,
96        ]
97    }
98}
99
100impl fmt::Display for ProjectKind {
101    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102        let name = match self {
103            Self::Rust => "Rust",
104            Self::Node => "Node.js",
105            Self::Python => "Python",
106            Self::Java => "Java",
107            Self::DotNet => ".NET",
108            Self::Go => "Go",
109            Self::Zig => "Zig",
110            Self::CMake => "CMake",
111            Self::Swift => "Swift",
112            Self::Elixir => "Elixir",
113            Self::Haskell => "Haskell",
114            Self::Dart => "Dart",
115            Self::Ruby => "Ruby",
116            Self::Scala => "Scala",
117            Self::Unity => "Unity",
118            Self::Godot => "Godot",
119            Self::Terraform => "Terraform",
120        };
121        write!(f, "{name}")
122    }
123}
124
125/// A directory within a project that can be cleaned.
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct CleanTarget {
128    /// Absolute path to the cleanable directory.
129    pub path: PathBuf,
130    /// Display name (e.g. "node_modules", "target").
131    pub name: String,
132    /// Size in bytes.
133    pub size_bytes: u64,
134}
135
136/// A discovered developer project on disk.
137#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct ScannedProject {
139    /// The project root directory.
140    pub path: PathBuf,
141    /// The detected project kind.
142    pub kind: ProjectKind,
143    /// A human-friendly project name (usually the directory name).
144    pub name: String,
145    /// When the project was last modified (based on marker file).
146    pub last_modified: DateTime<Local>,
147    /// Directories that can be cleaned.
148    pub clean_targets: Vec<CleanTarget>,
149    /// Total reclaimable bytes across all clean targets.
150    pub total_cleanable_bytes: u64,
151}