Skip to main content

deps_dart/
error.rs

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