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