1use 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}