Skip to main content

clean_dev_dirs/config/
filter.rs

1//! Filtering configuration for project selection.
2//!
3//! This module defines the filtering options, project type filters, and sorting
4//! criteria used to determine which projects should be scanned, cleaned, and
5//! how they should be ordered in the output.
6
7use clap::ValueEnum;
8
9/// Enumeration of supported project type filters.
10///
11/// This enum is used to restrict scanning and cleaning to specific types of
12/// development projects.
13#[derive(Clone, Copy, PartialEq, Eq, Debug, ValueEnum, Default)]
14pub enum ProjectFilter {
15    /// Include all supported project types
16    #[default]
17    All,
18
19    /// Include only Rust projects (Cargo.toml + target/)
20    Rust,
21
22    /// Include only Node.js projects (package.json + `node_modules`/)
23    Node,
24
25    /// Include only Python projects (Python config files + cache dirs)
26    Python,
27
28    /// Include only Go projects (go.mod + vendor/)
29    Go,
30
31    /// Include only Java/Kotlin projects (pom.xml or build.gradle + target/ or build/)
32    Java,
33
34    /// Include only C/C++ projects (CMakeLists.txt or Makefile + build/)
35    Cpp,
36
37    /// Include only Swift projects (Package.swift + .build/)
38    Swift,
39
40    /// Include only .NET/C# projects (.csproj + bin/ + obj/)
41    #[value(name = "dotnet")]
42    DotNet,
43
44    /// Include only Ruby projects (Gemfile + .bundle/ or vendor/bundle/)
45    Ruby,
46
47    /// Include only Elixir projects (mix.exs + _build/)
48    Elixir,
49
50    /// Include only Deno projects (deno.json + vendor/ or `node_modules`/)
51    Deno,
52
53    /// Include only PHP projects (composer.json + vendor/)
54    #[value(name = "php")]
55    Php,
56
57    /// Include only Haskell projects (stack.yaml or cabal.project + .stack-work/ or dist-newstyle/)
58    Haskell,
59
60    /// Include only Dart/Flutter projects (pubspec.yaml + `.dart_tool`/ or build/)
61    Dart,
62
63    /// Include only Zig projects (build.zig + zig-cache/ or zig-out/)
64    Zig,
65
66    /// Include only Scala projects (build.sbt + target/)
67    Scala,
68}
69
70/// Configuration for project filtering criteria.
71///
72/// This struct contains the filtering options used to determine which projects
73/// should be considered for cleanup based on size and modification time.
74#[derive(Clone, Debug)]
75pub struct FilterOptions {
76    /// Minimum size threshold for build directories
77    pub keep_size: String,
78
79    /// Minimum age in days for projects to be considered
80    pub keep_days: u32,
81
82    /// Optional name pattern (glob or `regex:…` prefix) to filter projects by name
83    pub name_pattern: Option<String>,
84}
85
86/// Enumeration of supported sorting criteria for project output.
87///
88/// This enum determines how projects are ordered in the output.
89/// Each variant has a natural default direction:
90/// - `Size`: largest first (descending)
91/// - `Age`: oldest first (ascending)
92/// - `Name`: alphabetical (ascending)
93/// - `Type`: grouped by type name alphabetically
94#[derive(Clone, Copy, PartialEq, Eq, Debug, ValueEnum)]
95pub enum SortCriteria {
96    /// Sort by build artifacts size (largest first by default)
97    Size,
98
99    /// Sort by build artifacts modification time (oldest first by default)
100    Age,
101
102    /// Sort by project name alphabetically (A-Z by default)
103    Name,
104
105    /// Sort by project type name alphabetically
106    Type,
107}
108
109/// Configuration for project sorting behavior.
110///
111/// Controls how the list of projects is ordered before display or processing.
112/// When `criteria` is `None`, projects are displayed in scan order.
113#[derive(Clone, Debug)]
114pub struct SortOptions {
115    /// The sorting criterion to apply, or `None` to preserve scan order
116    pub criteria: Option<SortCriteria>,
117
118    /// Whether to reverse the sort order
119    pub reverse: bool,
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn test_project_filter_equality() {
128        assert_eq!(ProjectFilter::All, ProjectFilter::All);
129        assert_eq!(ProjectFilter::Rust, ProjectFilter::Rust);
130        assert_eq!(ProjectFilter::Node, ProjectFilter::Node);
131        assert_eq!(ProjectFilter::Python, ProjectFilter::Python);
132        assert_eq!(ProjectFilter::Go, ProjectFilter::Go);
133        assert_eq!(ProjectFilter::Java, ProjectFilter::Java);
134        assert_eq!(ProjectFilter::Cpp, ProjectFilter::Cpp);
135        assert_eq!(ProjectFilter::Swift, ProjectFilter::Swift);
136        assert_eq!(ProjectFilter::DotNet, ProjectFilter::DotNet);
137        assert_eq!(ProjectFilter::Ruby, ProjectFilter::Ruby);
138        assert_eq!(ProjectFilter::Elixir, ProjectFilter::Elixir);
139        assert_eq!(ProjectFilter::Deno, ProjectFilter::Deno);
140        assert_eq!(ProjectFilter::Php, ProjectFilter::Php);
141        assert_eq!(ProjectFilter::Haskell, ProjectFilter::Haskell);
142        assert_eq!(ProjectFilter::Dart, ProjectFilter::Dart);
143        assert_eq!(ProjectFilter::Zig, ProjectFilter::Zig);
144        assert_eq!(ProjectFilter::Scala, ProjectFilter::Scala);
145
146        assert_ne!(ProjectFilter::All, ProjectFilter::Rust);
147        assert_ne!(ProjectFilter::Rust, ProjectFilter::Node);
148        assert_ne!(ProjectFilter::Node, ProjectFilter::Python);
149        assert_ne!(ProjectFilter::Python, ProjectFilter::Go);
150        assert_ne!(ProjectFilter::Go, ProjectFilter::Java);
151        assert_ne!(ProjectFilter::Java, ProjectFilter::Cpp);
152        assert_ne!(ProjectFilter::Cpp, ProjectFilter::Swift);
153        assert_ne!(ProjectFilter::Swift, ProjectFilter::DotNet);
154        assert_ne!(ProjectFilter::DotNet, ProjectFilter::Ruby);
155        assert_ne!(ProjectFilter::Ruby, ProjectFilter::Elixir);
156        assert_ne!(ProjectFilter::Elixir, ProjectFilter::Deno);
157        assert_ne!(ProjectFilter::Deno, ProjectFilter::Php);
158        assert_ne!(ProjectFilter::Php, ProjectFilter::Haskell);
159        assert_ne!(ProjectFilter::Haskell, ProjectFilter::Dart);
160        assert_ne!(ProjectFilter::Dart, ProjectFilter::Zig);
161        assert_ne!(ProjectFilter::Zig, ProjectFilter::Scala);
162    }
163
164    #[test]
165    fn test_project_filter_copy() {
166        let original = ProjectFilter::Rust;
167        let copied = original;
168
169        assert_eq!(original, copied);
170    }
171
172    #[test]
173    fn test_project_filter_default() {
174        let default_filter = ProjectFilter::default();
175        assert_eq!(default_filter, ProjectFilter::All);
176    }
177
178    #[test]
179    fn test_filter_options_creation() {
180        let filter_opts = FilterOptions {
181            keep_size: "100MB".to_string(),
182            keep_days: 30,
183            name_pattern: None,
184        };
185
186        assert_eq!(filter_opts.keep_size, "100MB");
187        assert_eq!(filter_opts.keep_days, 30);
188        assert!(filter_opts.name_pattern.is_none());
189    }
190
191    #[test]
192    fn test_filter_options_clone() {
193        let original = FilterOptions {
194            keep_size: "100MB".to_string(),
195            keep_days: 30,
196            name_pattern: None,
197        };
198        let cloned = original.clone();
199
200        assert_eq!(original.keep_size, cloned.keep_size);
201        assert_eq!(original.keep_days, cloned.keep_days);
202        assert_eq!(original.name_pattern, cloned.name_pattern);
203    }
204
205    #[test]
206    fn test_filter_options_name_pattern() {
207        let with_glob = FilterOptions {
208            keep_size: "0".to_string(),
209            keep_days: 0,
210            name_pattern: Some("my-app*".to_string()),
211        };
212        assert_eq!(with_glob.name_pattern.as_deref(), Some("my-app*"));
213
214        let with_regex = FilterOptions {
215            keep_size: "0".to_string(),
216            keep_days: 0,
217            name_pattern: Some("regex:^client-.*".to_string()),
218        };
219        assert_eq!(with_regex.name_pattern.as_deref(), Some("regex:^client-.*"));
220    }
221
222    #[test]
223    fn test_sort_criteria_equality() {
224        assert_eq!(SortCriteria::Size, SortCriteria::Size);
225        assert_eq!(SortCriteria::Age, SortCriteria::Age);
226        assert_eq!(SortCriteria::Name, SortCriteria::Name);
227        assert_eq!(SortCriteria::Type, SortCriteria::Type);
228
229        assert_ne!(SortCriteria::Size, SortCriteria::Age);
230        assert_ne!(SortCriteria::Name, SortCriteria::Type);
231    }
232
233    #[test]
234    fn test_sort_criteria_copy() {
235        let original = SortCriteria::Size;
236        let copied = original;
237        assert_eq!(original, copied);
238    }
239
240    #[test]
241    fn test_sort_options_creation() {
242        let sort_opts = SortOptions {
243            criteria: Some(SortCriteria::Size),
244            reverse: false,
245        };
246        assert_eq!(sort_opts.criteria, Some(SortCriteria::Size));
247        assert!(!sort_opts.reverse);
248    }
249
250    #[test]
251    fn test_sort_options_none_criteria() {
252        let sort_opts = SortOptions {
253            criteria: None,
254            reverse: false,
255        };
256        assert!(sort_opts.criteria.is_none());
257    }
258
259    #[test]
260    fn test_sort_options_clone() {
261        let original = SortOptions {
262            criteria: Some(SortCriteria::Age),
263            reverse: true,
264        };
265        let cloned = original.clone();
266
267        assert_eq!(original.criteria, cloned.criteria);
268        assert_eq!(original.reverse, cloned.reverse);
269    }
270}