Skip to main content

changeset_operations/traits/
inherited_version_checker.rs

1use std::path::Path;
2
3use changeset_core::PackageInfo;
4
5use crate::Result;
6
7pub trait InheritedVersionChecker: Send + Sync {
8    /// # Errors
9    ///
10    /// Propagates manifest read errors.
11    fn has_inherited_version(&self, manifest_path: &Path) -> Result<bool>;
12
13    /// # Errors
14    ///
15    /// Propagates manifest read errors.
16    fn find_packages_with_inherited_versions(
17        &self,
18        packages: &[PackageInfo],
19    ) -> Result<Vec<String>> {
20        let mut inherited = Vec::new();
21        for pkg in packages {
22            let manifest_path = pkg.path().join("Cargo.toml");
23            if self.has_inherited_version(&manifest_path)? {
24                inherited.push(pkg.name().clone());
25            }
26        }
27        Ok(inherited)
28    }
29}
30
31#[cfg(test)]
32mod tests {
33    use super::*;
34    use std::path::PathBuf;
35
36    struct TestChecker {
37        inherited: std::collections::HashSet<PathBuf>,
38        fail_on: Option<PathBuf>,
39    }
40
41    impl TestChecker {
42        fn new() -> Self {
43            Self {
44                inherited: std::collections::HashSet::new(),
45                fail_on: None,
46            }
47        }
48
49        fn with_inherited(mut self, path: PathBuf) -> Self {
50            self.inherited.insert(path);
51            self
52        }
53
54        fn failing_on(mut self, path: PathBuf) -> Self {
55            self.fail_on = Some(path);
56            self
57        }
58    }
59
60    impl InheritedVersionChecker for TestChecker {
61        fn has_inherited_version(&self, manifest_path: &Path) -> Result<bool> {
62            if let Some(ref fail_path) = self.fail_on {
63                if manifest_path == fail_path {
64                    return Err(crate::OperationError::Io(std::io::Error::new(
65                        std::io::ErrorKind::PermissionDenied,
66                        "mock failure",
67                    )));
68                }
69            }
70            Ok(self.inherited.contains(manifest_path))
71        }
72    }
73
74    fn make_package(name: &str, path: &str) -> PackageInfo {
75        PackageInfo::new(
76            name.to_string(),
77            "1.0.0".parse().expect("valid version"),
78            PathBuf::from(path),
79        )
80    }
81
82    #[test]
83    fn find_packages_returns_empty_for_no_inherited() {
84        let checker = TestChecker::new();
85        let packages = vec![make_package("crate-a", "/pkg/a")];
86
87        let result = checker
88            .find_packages_with_inherited_versions(&packages)
89            .expect("should succeed");
90
91        assert!(result.is_empty());
92    }
93
94    #[test]
95    fn find_packages_returns_inherited_package_names() {
96        let checker = TestChecker::new()
97            .with_inherited(PathBuf::from("/pkg/a/Cargo.toml"))
98            .with_inherited(PathBuf::from("/pkg/c/Cargo.toml"));
99
100        let packages = vec![
101            make_package("crate-a", "/pkg/a"),
102            make_package("crate-b", "/pkg/b"),
103            make_package("crate-c", "/pkg/c"),
104        ];
105
106        let result = checker
107            .find_packages_with_inherited_versions(&packages)
108            .expect("should succeed");
109
110        assert_eq!(result, vec!["crate-a", "crate-c"]);
111    }
112
113    #[test]
114    fn find_packages_propagates_errors() {
115        let checker = TestChecker::new().failing_on(PathBuf::from("/pkg/b/Cargo.toml"));
116
117        let packages = vec![
118            make_package("crate-a", "/pkg/a"),
119            make_package("crate-b", "/pkg/b"),
120        ];
121
122        let result = checker.find_packages_with_inherited_versions(&packages);
123
124        assert!(result.is_err());
125    }
126}