Skip to main content

deps_go/
error.rs

1//! Errors specific to Go module dependency handling.
2
3use thiserror::Error;
4
5/// Errors that can occur during Go module operations.
6#[derive(Error, Debug)]
7pub enum GoError {
8    /// Failed to parse go.mod file
9    #[error("Failed to parse go.mod: {source}")]
10    ParseError {
11        #[source]
12        source: Box<dyn std::error::Error + Send + Sync>,
13    },
14
15    /// Invalid version specifier
16    #[error("Invalid version specifier '{specifier}': {message}")]
17    InvalidVersionSpecifier { specifier: String, message: String },
18
19    /// Module not found in registry
20    #[error("Module '{module}' not found")]
21    ModuleNotFound { module: String },
22
23    /// Registry request failed
24    #[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    /// Cache error
32    #[error("Cache error: {0}")]
33    CacheError(String),
34
35    /// Invalid module path
36    #[error("Invalid module path: {0}")]
37    InvalidModulePath(String),
38
39    /// Invalid pseudo-version format
40    #[error("Invalid pseudo-version '{version}': {reason}")]
41    InvalidPseudoVersion { version: String, reason: String },
42
43    /// Failed to deserialize proxy.golang.org API response
44    #[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    /// I/O error
52    #[error("I/O error: {0}")]
53    Io(#[from] std::io::Error),
54
55    /// Generic error wrapper
56    #[error(transparent)]
57    Other(#[from] Box<dyn std::error::Error + Send + Sync>),
58}
59
60/// Result type alias for Go operations.
61pub type Result<T> = std::result::Result<T, GoError>;
62
63impl GoError {
64    /// Helper for creating registry errors
65    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    /// Helper for creating invalid version specifier errors
76    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    /// Helper for creating module not found errors
87    pub fn module_not_found(module: impl Into<String>) -> Self {
88        Self::ModuleNotFound {
89            module: module.into(),
90        }
91    }
92
93    /// Helper for creating invalid pseudo-version errors
94    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}