Skip to main content

changepacks_cli/options/
filter_options.rs

1use changepacks_core::Project;
2use clap::ValueEnum;
3
4/// CLI filter for workspace-only or package-only listing.
5///
6/// Used by the check command to filter projects by type.
7#[derive(Debug, Clone, ValueEnum)]
8pub enum FilterOptions {
9    /// Show only workspace projects
10    Workspace,
11    /// Show only package projects
12    Package,
13}
14
15impl FilterOptions {
16    #[must_use]
17    pub fn matches(&self, project: &Project) -> bool {
18        match self {
19            Self::Workspace => matches!(project, Project::Workspace(_)),
20            Self::Package => matches!(project, Project::Package(_)),
21        }
22    }
23}
24
25#[cfg(test)]
26mod tests {
27    use super::*;
28    use async_trait::async_trait;
29    use changepacks_core::{Language, Package, UpdateType, Workspace};
30    use clap::ValueEnum;
31    use std::collections::HashSet;
32    use std::path::{Path, PathBuf};
33
34    #[derive(Debug)]
35    struct MockPackage {
36        name: Option<String>,
37        path: PathBuf,
38        relative_path: PathBuf,
39        version: Option<String>,
40        language: Language,
41        dependencies: HashSet<String>,
42        changed: bool,
43    }
44
45    #[async_trait]
46    impl Package for MockPackage {
47        fn name(&self) -> Option<&str> {
48            self.name.as_deref()
49        }
50
51        fn version(&self) -> Option<&str> {
52            self.version.as_deref()
53        }
54
55        fn path(&self) -> &Path {
56            &self.path
57        }
58
59        fn relative_path(&self) -> &Path {
60            &self.relative_path
61        }
62
63        async fn update_version(&mut self, _update_type: UpdateType) -> anyhow::Result<()> {
64            Ok(())
65        }
66
67        fn is_changed(&self) -> bool {
68            self.changed
69        }
70
71        fn language(&self) -> Language {
72            self.language
73        }
74
75        fn dependencies(&self) -> &HashSet<String> {
76            &self.dependencies
77        }
78
79        fn add_dependency(&mut self, dependency: &str) {
80            self.dependencies.insert(dependency.to_string());
81        }
82
83        fn set_changed(&mut self, changed: bool) {
84            self.changed = changed;
85        }
86
87        fn default_publish_command(&self) -> String {
88            "echo publish".to_string()
89        }
90    }
91
92    #[derive(Debug)]
93    struct MockWorkspace {
94        name: Option<String>,
95        path: PathBuf,
96        relative_path: PathBuf,
97        version: Option<String>,
98        language: Language,
99        dependencies: HashSet<String>,
100        changed: bool,
101    }
102
103    #[async_trait]
104    impl Workspace for MockWorkspace {
105        fn name(&self) -> Option<&str> {
106            self.name.as_deref()
107        }
108
109        fn version(&self) -> Option<&str> {
110            self.version.as_deref()
111        }
112
113        fn path(&self) -> &Path {
114            &self.path
115        }
116
117        fn relative_path(&self) -> &Path {
118            &self.relative_path
119        }
120
121        async fn update_version(&mut self, _update_type: UpdateType) -> anyhow::Result<()> {
122            Ok(())
123        }
124
125        fn is_changed(&self) -> bool {
126            self.changed
127        }
128
129        fn language(&self) -> Language {
130            self.language
131        }
132
133        fn dependencies(&self) -> &HashSet<String> {
134            &self.dependencies
135        }
136
137        fn add_dependency(&mut self, dependency: &str) {
138            self.dependencies.insert(dependency.to_string());
139        }
140
141        fn set_changed(&mut self, changed: bool) {
142            self.changed = changed;
143        }
144
145        fn default_publish_command(&self) -> String {
146            "echo publish".to_string()
147        }
148
149        async fn update_workspace_dependencies(
150            &self,
151            _packages: &[&dyn Package],
152        ) -> anyhow::Result<()> {
153            Ok(())
154        }
155    }
156
157    fn workspace_project() -> Project {
158        Project::Workspace(Box::new(MockWorkspace {
159            name: Some("workspace".to_string()),
160            path: PathBuf::from("/repo/package.json"),
161            relative_path: PathBuf::from("package.json"),
162            version: Some("1.0.0".to_string()),
163            language: Language::Node,
164            dependencies: HashSet::new(),
165            changed: false,
166        }))
167    }
168
169    fn package_project() -> Project {
170        Project::Package(Box::new(MockPackage {
171            name: Some("package".to_string()),
172            path: PathBuf::from("/repo/crates/pkg/Cargo.toml"),
173            relative_path: PathBuf::from("crates/pkg/Cargo.toml"),
174            version: Some("1.0.0".to_string()),
175            language: Language::Rust,
176            dependencies: HashSet::new(),
177            changed: false,
178        }))
179    }
180
181    #[test]
182    fn test_filter_options_matches_workspace_with_workspace_project() {
183        let project = workspace_project();
184        assert!(FilterOptions::Workspace.matches(&project));
185    }
186
187    #[test]
188    fn test_filter_options_matches_workspace_with_package_project() {
189        let project = package_project();
190        assert!(!FilterOptions::Workspace.matches(&project));
191    }
192
193    #[test]
194    fn test_filter_options_matches_package_with_package_project() {
195        let project = package_project();
196        assert!(FilterOptions::Package.matches(&project));
197    }
198
199    #[test]
200    fn test_filter_options_matches_package_with_workspace_project() {
201        let project = workspace_project();
202        assert!(!FilterOptions::Package.matches(&project));
203    }
204
205    #[test]
206    fn test_filter_options_value_enum_workspace() {
207        let filter = FilterOptions::from_str("workspace", true).unwrap();
208        assert!(matches!(filter, FilterOptions::Workspace));
209    }
210
211    #[test]
212    fn test_filter_options_value_enum_package() {
213        let filter = FilterOptions::from_str("package", true).unwrap();
214        assert!(matches!(filter, FilterOptions::Package));
215    }
216}