Skip to main content

dependency_check_updates_core/
error.rs

1use miette::Diagnostic;
2use std::path::PathBuf;
3use thiserror::Error;
4
5/// Main error type for dependency-check-updates operations.
6#[derive(Debug, Error, Diagnostic)]
7pub enum DcuError {
8    #[error("failed to read manifest at {path}")]
9    #[diagnostic(
10        code(dependency_check_updates::io_error),
11        help("make sure the file exists and is readable")
12    )]
13    Io {
14        path: PathBuf,
15        #[source]
16        source: std::io::Error,
17    },
18
19    #[error("failed to parse manifest at {path}")]
20    #[diagnostic(code(dependency_check_updates::parse_error))]
21    ManifestParse { path: PathBuf, detail: String },
22
23    #[error("registry lookup failed for package `{package}`: {detail}")]
24    #[diagnostic(
25        code(dependency_check_updates::registry_error),
26        help("check your internet connection, or set GITHUB_TOKEN if scanning workflows")
27    )]
28    RegistryLookup { package: String, detail: String },
29
30    #[error("failed to apply patch to {path}")]
31    #[diagnostic(code(dependency_check_updates::patch_error))]
32    PatchFailed { path: PathBuf, detail: String },
33
34    #[error("invalid semver: {input}")]
35    #[diagnostic(code(dependency_check_updates::semver_error))]
36    SemverParse { input: String, detail: String },
37
38    #[error("no manifest found in {path}")]
39    #[diagnostic(
40        code(dependency_check_updates::no_manifest),
41        help(
42            "run dependency-check-updates in a directory containing package.json, or use --manifest"
43        )
44    )]
45    NoManifest { path: PathBuf },
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51
52    #[test]
53    fn test_dcu_error_io() {
54        let path = PathBuf::from("test.json");
55        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
56        let err = DcuError::Io {
57            path: path.clone(),
58            source: io_err,
59        };
60        assert_eq!(
61            err.to_string(),
62            format!("failed to read manifest at {}", path.display())
63        );
64    }
65
66    #[test]
67    fn test_dcu_error_manifest_parse() {
68        let path = PathBuf::from("package.json");
69        let err = DcuError::ManifestParse {
70            path: path.clone(),
71            detail: "unexpected token".to_string(),
72        };
73        assert_eq!(
74            err.to_string(),
75            format!("failed to parse manifest at {}", path.display())
76        );
77    }
78
79    #[test]
80    fn test_dcu_error_no_manifest() {
81        let path = PathBuf::from("/some/dir");
82        let err = DcuError::NoManifest { path: path.clone() };
83        assert_eq!(
84            err.to_string(),
85            format!("no manifest found in {}", path.display())
86        );
87    }
88
89    #[test]
90    fn test_dcu_error_registry_lookup() {
91        let err = DcuError::RegistryLookup {
92            package: "lodash".to_string(),
93            detail: "connection timeout".to_string(),
94        };
95        // Display now surfaces `detail` — without it, users hit a dead-end
96        // when GitHub Tags API rate-limits them (no hint to set GITHUB_TOKEN).
97        assert_eq!(
98            err.to_string(),
99            "registry lookup failed for package `lodash`: connection timeout"
100        );
101    }
102
103    #[test]
104    fn test_dcu_error_semver_parse() {
105        let err = DcuError::SemverParse {
106            input: "not.a.version".to_string(),
107            detail: "invalid semver format".to_string(),
108        };
109        assert_eq!(err.to_string(), "invalid semver: not.a.version");
110    }
111}