Skip to main content

deps_maven/
error.rs

1//! Errors specific to Maven/pom.xml dependency handling.
2
3use thiserror::Error;
4
5#[derive(Error, Debug)]
6pub enum MavenError {
7    #[error("Failed to parse pom.xml: {message}")]
8    ParseError { message: String },
9
10    #[error("Invalid version '{version}': {message}")]
11    InvalidVersion { version: String, message: String },
12
13    #[error("Package '{package}' not found on Maven Central")]
14    PackageNotFound { package: String },
15
16    #[error("Maven Central request failed for '{package}': {source}")]
17    RegistryError {
18        package: String,
19        #[source]
20        source: Box<dyn std::error::Error + Send + Sync>,
21    },
22
23    #[error("Failed to parse Maven Central API response for '{package}': {source}")]
24    ApiResponseError {
25        package: String,
26        #[source]
27        source: serde_json::Error,
28    },
29
30    #[error("Invalid Maven coordinates '{coordinates}': expected 'groupId:artifactId'")]
31    InvalidCoordinates { coordinates: String },
32
33    #[error("Cache error: {0}")]
34    CacheError(String),
35
36    #[error("I/O error: {0}")]
37    Io(#[from] std::io::Error),
38
39    #[error(transparent)]
40    Other(#[from] Box<dyn std::error::Error + Send + Sync>),
41}
42
43pub type Result<T> = std::result::Result<T, MavenError>;
44
45impl From<deps_core::DepsError> for MavenError {
46    fn from(err: deps_core::DepsError) -> Self {
47        match err {
48            deps_core::DepsError::ParseError { source, .. } => Self::CacheError(source.to_string()),
49            deps_core::DepsError::CacheError(msg) => Self::CacheError(msg),
50            deps_core::DepsError::InvalidVersionReq(msg) => Self::InvalidVersion {
51                version: String::new(),
52                message: msg,
53            },
54            deps_core::DepsError::Io(e) => Self::Io(e),
55            deps_core::DepsError::Json(e) => Self::ApiResponseError {
56                package: String::new(),
57                source: e,
58            },
59            other => Self::CacheError(other.to_string()),
60        }
61    }
62}
63
64impl From<MavenError> for deps_core::DepsError {
65    fn from(err: MavenError) -> Self {
66        match err {
67            MavenError::ParseError { message } => Self::ParseError {
68                file_type: "pom.xml".into(),
69                source: Box::new(std::io::Error::other(message)),
70            },
71            MavenError::InvalidVersion { message, .. } => Self::InvalidVersionReq(message),
72            MavenError::PackageNotFound { package } => {
73                Self::CacheError(format!("Package '{package}' not found"))
74            }
75            MavenError::RegistryError { package, source } => Self::ParseError {
76                file_type: format!("Maven Central for {package}"),
77                source,
78            },
79            MavenError::ApiResponseError { source, .. } => Self::Json(source),
80            MavenError::InvalidCoordinates { coordinates } => {
81                Self::CacheError(format!("Invalid coordinates: {coordinates}"))
82            }
83            MavenError::CacheError(msg) => Self::CacheError(msg),
84            MavenError::Io(e) => Self::Io(e),
85            MavenError::Other(e) => Self::CacheError(e.to_string()),
86        }
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_error_display() {
96        let err = MavenError::PackageNotFound {
97            package: "org.apache.commons:commons-lang3".into(),
98        };
99        assert_eq!(
100            err.to_string(),
101            "Package 'org.apache.commons:commons-lang3' not found on Maven Central"
102        );
103
104        let err = MavenError::InvalidCoordinates {
105            coordinates: "badcoords".into(),
106        };
107        assert!(err.to_string().contains("badcoords"));
108    }
109
110    #[test]
111    fn test_conversion_to_deps_error() {
112        let err = MavenError::PackageNotFound {
113            package: "test:pkg".into(),
114        };
115        let deps_err: deps_core::DepsError = err.into();
116        assert!(deps_err.to_string().contains("not found"));
117    }
118
119    #[test]
120    fn test_parse_error_to_deps_error() {
121        let err = MavenError::ParseError {
122            message: "syntax error".into(),
123        };
124        let deps_err: deps_core::DepsError = err.into();
125        assert!(matches!(deps_err, deps_core::DepsError::ParseError { .. }));
126    }
127
128    #[test]
129    fn test_invalid_version_to_deps_error() {
130        let err = MavenError::InvalidVersion {
131            version: "bad".into(),
132            message: "invalid".into(),
133        };
134        let deps_err: deps_core::DepsError = err.into();
135        assert!(matches!(
136            deps_err,
137            deps_core::DepsError::InvalidVersionReq(_)
138        ));
139    }
140
141    #[test]
142    fn test_io_error_conversion() {
143        let io_err = std::io::Error::from(std::io::ErrorKind::NotFound);
144        let err: MavenError = io_err.into();
145        assert!(matches!(err, MavenError::Io(_)));
146
147        let deps_err: deps_core::DepsError = err.into();
148        assert!(matches!(deps_err, deps_core::DepsError::Io(_)));
149    }
150
151    #[test]
152    fn test_deps_error_to_maven_error() {
153        let deps_err = deps_core::DepsError::CacheError("cache miss".into());
154        let maven_err: MavenError = deps_err.into();
155        assert!(matches!(maven_err, MavenError::CacheError(_)));
156
157        let deps_err = deps_core::DepsError::InvalidVersionReq("bad".into());
158        let maven_err: MavenError = deps_err.into();
159        assert!(matches!(maven_err, MavenError::InvalidVersion { .. }));
160    }
161
162    #[test]
163    fn test_api_response_error_to_deps_error() {
164        let json_err = serde_json::from_str::<serde_json::Value>("{invalid}").unwrap_err();
165        let err = MavenError::ApiResponseError {
166            package: "test:pkg".into(),
167            source: json_err,
168        };
169        let deps_err: deps_core::DepsError = err.into();
170        assert!(matches!(deps_err, deps_core::DepsError::Json(_)));
171    }
172
173    #[test]
174    fn test_other_error_to_deps_error() {
175        let other: Box<dyn std::error::Error + Send + Sync> =
176            Box::new(std::io::Error::other("unknown"));
177        let err = MavenError::Other(other);
178        let deps_err: deps_core::DepsError = err.into();
179        assert!(matches!(deps_err, deps_core::DepsError::CacheError(_)));
180    }
181}