1use thiserror::Error;
4
5#[derive(Error, Debug)]
7pub enum GoError {
8 #[error("Failed to parse go.mod: {source}")]
10 ParseError {
11 #[source]
12 source: Box<dyn std::error::Error + Send + Sync>,
13 },
14
15 #[error("Invalid version specifier '{specifier}': {message}")]
17 InvalidVersionSpecifier { specifier: String, message: String },
18
19 #[error("Module '{module}' not found")]
21 ModuleNotFound { module: String },
22
23 #[error("Registry request failed for '{module}': {source}")]
25 RegistryError {
26 module: String,
27 #[source]
28 source: Box<dyn std::error::Error + Send + Sync>,
29 },
30
31 #[error("Cache error: {0}")]
33 CacheError(String),
34
35 #[error("Invalid module path: {0}")]
37 InvalidModulePath(String),
38
39 #[error("Invalid pseudo-version '{version}': {reason}")]
41 InvalidPseudoVersion { version: String, reason: String },
42
43 #[error("Failed to parse proxy.golang.org API response for '{module}': {source}")]
45 ApiResponseError {
46 module: String,
47 #[source]
48 source: serde_json::Error,
49 },
50
51 #[error("I/O error: {0}")]
53 Io(#[from] std::io::Error),
54
55 #[error(transparent)]
57 Other(#[from] Box<dyn std::error::Error + Send + Sync>),
58}
59
60pub type Result<T> = std::result::Result<T, GoError>;
62
63impl GoError {
64 pub fn registry_error(
66 module: impl Into<String>,
67 error: impl std::error::Error + Send + Sync + 'static,
68 ) -> Self {
69 Self::RegistryError {
70 module: module.into(),
71 source: Box::new(error),
72 }
73 }
74
75 pub fn invalid_version_specifier(
77 specifier: impl Into<String>,
78 message: impl Into<String>,
79 ) -> Self {
80 Self::InvalidVersionSpecifier {
81 specifier: specifier.into(),
82 message: message.into(),
83 }
84 }
85
86 pub fn module_not_found(module: impl Into<String>) -> Self {
88 Self::ModuleNotFound {
89 module: module.into(),
90 }
91 }
92
93 pub fn invalid_pseudo_version(version: impl Into<String>, reason: impl Into<String>) -> Self {
95 Self::InvalidPseudoVersion {
96 version: version.into(),
97 reason: reason.into(),
98 }
99 }
100}
101
102impl From<GoError> for deps_core::DepsError {
103 fn from(err: GoError) -> Self {
104 match err {
105 GoError::ParseError { source } => Self::ParseError {
106 file_type: "go.mod".into(),
107 source,
108 },
109 GoError::InvalidVersionSpecifier { message, .. } => Self::InvalidVersionReq(message),
110 GoError::ModuleNotFound { module } => {
111 Self::CacheError(format!("Module '{module}' not found"))
112 }
113 GoError::RegistryError { module, source } => Self::ParseError {
114 file_type: format!("registry for {module}"),
115 source,
116 },
117 GoError::CacheError(msg) => Self::CacheError(msg),
118 GoError::InvalidModulePath(msg) => Self::InvalidVersionReq(msg),
119 GoError::InvalidPseudoVersion { version, reason } => {
120 Self::InvalidVersionReq(format!("{version}: {reason}"))
121 }
122 GoError::ApiResponseError { module: _, source } => Self::Json(source),
123 GoError::Io(e) => Self::Io(e),
124 GoError::Other(e) => Self::ParseError {
125 file_type: "go".into(),
126 source: e,
127 },
128 }
129 }
130}
131
132impl From<deps_core::DepsError> for GoError {
133 fn from(err: deps_core::DepsError) -> Self {
134 match err {
135 deps_core::DepsError::ParseError { source, .. } => Self::ParseError { source },
136 deps_core::DepsError::CacheError(msg) => Self::CacheError(msg),
137 deps_core::DepsError::InvalidVersionReq(msg) => Self::InvalidVersionSpecifier {
138 specifier: String::new(),
139 message: msg,
140 },
141 deps_core::DepsError::Io(e) => Self::Io(e),
142 deps_core::DepsError::Json(e) => Self::ApiResponseError {
143 module: String::new(),
144 source: e,
145 },
146 other => Self::CacheError(other.to_string()),
147 }
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn test_error_construction() {
157 let err = GoError::ModuleNotFound {
158 module: "test/module".to_string(),
159 };
160 assert_eq!(err.to_string(), "Module 'test/module' not found");
161 }
162
163 #[test]
164 fn test_error_conversion() {
165 let go_err = GoError::InvalidModulePath("invalid".to_string());
166 let deps_err: deps_core::DepsError = go_err.into();
167 assert!(matches!(
168 deps_err,
169 deps_core::DepsError::InvalidVersionReq(_)
170 ));
171 }
172
173 #[test]
174 fn test_parse_error_conversion() {
175 let go_err = GoError::ParseError {
176 source: Box::new(std::io::Error::other("parse failed")),
177 };
178 let deps_err: deps_core::DepsError = go_err.into();
179 assert!(matches!(deps_err, deps_core::DepsError::ParseError { .. }));
180 }
181
182 #[test]
183 fn test_registry_error_conversion() {
184 let go_err = GoError::RegistryError {
185 module: "test/module".to_string(),
186 source: Box::new(std::io::Error::other("network failed")),
187 };
188 let deps_err: deps_core::DepsError = go_err.into();
189 assert!(matches!(deps_err, deps_core::DepsError::ParseError { .. }));
190 }
191
192 #[test]
193 fn test_io_error_conversion() {
194 let go_err = GoError::Io(std::io::Error::new(
195 std::io::ErrorKind::NotFound,
196 "not found",
197 ));
198 let deps_err: deps_core::DepsError = go_err.into();
199 assert!(matches!(deps_err, deps_core::DepsError::Io(_)));
200 }
201
202 #[test]
203 fn test_cache_error_conversion() {
204 let go_err = GoError::CacheError("cache miss".to_string());
205 let deps_err: deps_core::DepsError = go_err.into();
206 assert!(matches!(deps_err, deps_core::DepsError::CacheError(_)));
207 }
208
209 #[test]
210 fn test_bidirectional_conversion() {
211 let deps_err = deps_core::DepsError::CacheError("test error".to_string());
212 let go_err: GoError = deps_err.into();
213 assert!(matches!(go_err, GoError::CacheError(_)));
214 }
215
216 #[test]
217 fn test_helper_methods() {
218 let err = GoError::registry_error("test/module", std::io::Error::other("fail"));
219 assert!(matches!(err, GoError::RegistryError { .. }));
220
221 let err = GoError::invalid_version_specifier("v1.0", "invalid");
222 assert!(matches!(err, GoError::InvalidVersionSpecifier { .. }));
223
224 let err = GoError::module_not_found("test/module");
225 assert!(matches!(err, GoError::ModuleNotFound { .. }));
226
227 let err = GoError::invalid_pseudo_version("v0.0.0", "bad format");
228 assert!(matches!(err, GoError::InvalidPseudoVersion { .. }));
229 }
230}