1use crate::Progress;
5use tokio::sync::{AcquireError, watch};
6
7#[derive(Debug)]
13pub enum Error {
14 IoError(std::io::Error),
16 ConfigError(config::ConfigError),
18 JsonError(serde_json::Error),
20 HttpError(reqwest::Error),
22 MaxRetriesExceeded(u32),
24 UrlParseError(url::ParseError),
26 RpcError(i32, String),
28 InvalidRpcId(String),
30 EnvError(std::env::VarError),
32 SemaphoreError(AcquireError),
34 JoinError(tokio::task::JoinError),
36 ProgressSendError(watch::error::SendError<Progress>),
38 ProgressRecvError(watch::error::RecvError),
40 StripPrefixError(std::path::StripPrefixError),
42 ParseIntError(std::num::ParseIntError),
44 InvalidResponse,
46 NotImplemented,
48 PartTooLarge,
50 InvalidFileType(String),
52 InvalidAnnotationType(String),
54 UnsupportedFormat(String),
56 MissingImages(String),
58 MissingAnnotations(String),
60 MissingLabel(String),
62 InvalidParameters(String),
64 FeatureNotEnabled(String),
66 EmptyToken,
68 InvalidToken,
70 TokenExpired,
72 Unauthorized,
74 InvalidEtag(String),
76 StorageError(String),
78 #[cfg(feature = "polars")]
80 PolarsError(polars::error::PolarsError),
81 CocoError(String),
83 ZipError(String),
85}
86
87impl From<std::io::Error> for Error {
88 fn from(err: std::io::Error) -> Self {
89 Error::IoError(err)
90 }
91}
92
93impl From<config::ConfigError> for Error {
94 fn from(err: config::ConfigError) -> Self {
95 Error::ConfigError(err)
96 }
97}
98
99impl From<serde_json::Error> for Error {
100 fn from(err: serde_json::Error) -> Self {
101 Error::JsonError(err)
102 }
103}
104
105impl From<reqwest::Error> for Error {
106 fn from(err: reqwest::Error) -> Self {
107 Error::HttpError(err)
108 }
109}
110
111impl From<url::ParseError> for Error {
112 fn from(err: url::ParseError) -> Self {
113 Error::UrlParseError(err)
114 }
115}
116
117impl From<std::env::VarError> for Error {
118 fn from(err: std::env::VarError) -> Self {
119 Error::EnvError(err)
120 }
121}
122
123impl From<AcquireError> for Error {
124 fn from(err: AcquireError) -> Self {
125 Error::SemaphoreError(err)
126 }
127}
128
129impl From<tokio::task::JoinError> for Error {
130 fn from(err: tokio::task::JoinError) -> Self {
131 Error::JoinError(err)
132 }
133}
134
135impl From<watch::error::SendError<Progress>> for Error {
136 fn from(err: watch::error::SendError<Progress>) -> Self {
137 Error::ProgressSendError(err)
138 }
139}
140
141impl From<watch::error::RecvError> for Error {
142 fn from(err: watch::error::RecvError) -> Self {
143 Error::ProgressRecvError(err)
144 }
145}
146
147impl From<std::path::StripPrefixError> for Error {
148 fn from(err: std::path::StripPrefixError) -> Self {
149 Error::StripPrefixError(err)
150 }
151}
152
153impl From<std::num::ParseIntError> for Error {
154 fn from(err: std::num::ParseIntError) -> Self {
155 Error::ParseIntError(err)
156 }
157}
158
159impl From<crate::storage::StorageError> for Error {
160 fn from(err: crate::storage::StorageError) -> Self {
161 Error::StorageError(err.to_string())
162 }
163}
164
165#[cfg(feature = "polars")]
166impl From<polars::error::PolarsError> for Error {
167 fn from(err: polars::error::PolarsError) -> Self {
168 Error::PolarsError(err)
169 }
170}
171
172impl From<zip::result::ZipError> for Error {
173 fn from(err: zip::result::ZipError) -> Self {
174 Error::ZipError(err.to_string())
175 }
176}
177
178impl std::fmt::Display for Error {
179 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180 match self {
181 Error::IoError(e) => write!(f, "I/O error: {}", e),
182 Error::ConfigError(e) => write!(f, "Configuration error: {}", e),
183 Error::JsonError(e) => write!(f, "JSON error: {}", e),
184 Error::HttpError(e) => write!(f, "HTTP error: {}", e),
185 Error::MaxRetriesExceeded(n) => write!(f, "Maximum retries ({}) exceeded", n),
186 Error::UrlParseError(e) => write!(f, "URL parse error: {}", e),
187 Error::RpcError(code, msg) => write!(f, "RPC error {}: {}", code, msg),
188 Error::InvalidRpcId(id) => write!(f, "Invalid RPC ID: {}", id),
189 Error::EnvError(e) => write!(f, "Environment variable error: {}", e),
190 Error::SemaphoreError(e) => write!(f, "Semaphore error: {}", e),
191 Error::JoinError(e) => write!(f, "Task join error: {}", e),
192 Error::ProgressSendError(e) => write!(f, "Progress send error: {}", e),
193 Error::ProgressRecvError(e) => write!(f, "Progress receive error: {}", e),
194 Error::StripPrefixError(e) => write!(f, "Path prefix error: {}", e),
195 Error::ParseIntError(e) => write!(f, "Integer parse error: {}", e),
196 Error::InvalidResponse => write!(f, "Invalid server response"),
197 Error::NotImplemented => write!(f, "Not implemented"),
198 Error::PartTooLarge => write!(f, "File part size exceeds maximum limit"),
199 Error::InvalidFileType(s) => write!(f, "Invalid file type: {}", s),
200 Error::InvalidAnnotationType(s) => write!(f, "Invalid annotation type: {}", s),
201 Error::UnsupportedFormat(s) => write!(f, "Unsupported format: {}", s),
202 Error::MissingImages(s) => write!(f, "Missing images: {}", s),
203 Error::MissingAnnotations(s) => write!(f, "Missing annotations: {}", s),
204 Error::MissingLabel(s) => write!(f, "Missing label: {}", s),
205 Error::InvalidParameters(s) => write!(f, "Invalid parameters: {}", s),
206 Error::FeatureNotEnabled(s) => write!(f, "Feature not enabled: {}", s),
207 Error::EmptyToken => write!(f, "Authentication token is empty"),
208 Error::InvalidToken => write!(f, "Invalid authentication token"),
209 Error::TokenExpired => write!(f, "Authentication token has expired"),
210 Error::Unauthorized => write!(f, "Unauthorized access"),
211 Error::InvalidEtag(s) => write!(f, "Invalid ETag header: {}", s),
212 Error::StorageError(s) => write!(f, "Token storage error: {}", s),
213 #[cfg(feature = "polars")]
214 Error::PolarsError(e) => write!(f, "Polars error: {}", e),
215 Error::CocoError(s) => write!(f, "COCO format error: {}", s),
216 Error::ZipError(s) => write!(f, "ZIP error: {}", s),
217 }
218 }
219}
220
221impl std::error::Error for Error {
222 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
223 match self {
224 Error::IoError(e) => Some(e),
225 Error::ConfigError(e) => Some(e),
226 Error::JsonError(e) => Some(e),
227 Error::HttpError(e) => Some(e),
228 Error::UrlParseError(e) => Some(e),
229 Error::EnvError(e) => Some(e),
230 Error::JoinError(e) => Some(e),
231 Error::StripPrefixError(e) => Some(e),
232 Error::ParseIntError(e) => Some(e),
233 #[cfg(feature = "polars")]
234 Error::PolarsError(e) => Some(e),
235 _ => None,
236 }
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243 use std::path::Path;
244
245 #[test]
253 fn test_io_error_wrapping() {
254 let inner_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
256 let inner_str = inner_err.to_string();
258 let wrapped_err: Error = inner_err.into();
260 let wrapped_str = wrapped_err.to_string();
262 assert!(
264 wrapped_str.contains(&inner_str),
265 "Wrapped error '{}' should contain inner error '{}'",
266 wrapped_str,
267 inner_str
268 );
269 assert!(wrapped_str.starts_with("I/O error: "));
270 }
271
272 #[test]
273 fn test_config_error_wrapping() {
274 #[derive(Debug, serde::Deserialize)]
277 #[allow(dead_code)]
278 struct RequiredField {
279 required: String,
280 }
281
282 let inner_err = config::Config::builder()
283 .build()
284 .unwrap()
285 .try_deserialize::<RequiredField>()
286 .unwrap_err();
287 let inner_str = inner_err.to_string();
289 let wrapped_err: Error = inner_err.into();
291 let wrapped_str = wrapped_err.to_string();
293 assert!(
295 wrapped_str.contains(&inner_str),
296 "Wrapped error '{}' should contain inner error '{}'",
297 wrapped_str,
298 inner_str
299 );
300 assert!(wrapped_str.starts_with("Configuration error: "));
301 }
302
303 #[test]
304 fn test_json_error_wrapping() {
305 let inner_err = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
307 let inner_str = inner_err.to_string();
309 let wrapped_err: Error = inner_err.into();
311 let wrapped_str = wrapped_err.to_string();
313 assert!(
315 wrapped_str.contains(&inner_str),
316 "Wrapped error '{}' should contain inner error '{}'",
317 wrapped_str,
318 inner_str
319 );
320 assert!(wrapped_str.starts_with("JSON error: "));
321 }
322
323 #[test]
324 fn test_url_parse_error_wrapping() {
325 let inner_err = url::Url::parse("not a valid url").unwrap_err();
327 let inner_str = inner_err.to_string();
329 let wrapped_err: Error = inner_err.into();
331 let wrapped_str = wrapped_err.to_string();
333 assert!(
335 wrapped_str.contains(&inner_str),
336 "Wrapped error '{}' should contain inner error '{}'",
337 wrapped_str,
338 inner_str
339 );
340 assert!(wrapped_str.starts_with("URL parse error: "));
341 }
342
343 #[test]
344 fn test_env_error_wrapping() {
345 let inner_err = std::env::var("NONEXISTENT_VAR_12345").unwrap_err();
347 let inner_str = inner_err.to_string();
349 let wrapped_err: Error = inner_err.into();
351 let wrapped_str = wrapped_err.to_string();
353 assert!(
355 wrapped_str.contains(&inner_str),
356 "Wrapped error '{}' should contain inner error '{}'",
357 wrapped_str,
358 inner_str
359 );
360 assert!(wrapped_str.starts_with("Environment variable error: "));
361 }
362
363 #[test]
364 fn test_strip_prefix_error_wrapping() {
365 let path = Path::new("/foo/bar");
367 let prefix = Path::new("/baz");
368 let inner_err = path.strip_prefix(prefix).unwrap_err();
369 let inner_str = inner_err.to_string();
371 let wrapped_err: Error = inner_err.into();
373 let wrapped_str = wrapped_err.to_string();
375 assert!(
377 wrapped_str.contains(&inner_str),
378 "Wrapped error '{}' should contain inner error '{}'",
379 wrapped_str,
380 inner_str
381 );
382 assert!(wrapped_str.starts_with("Path prefix error: "));
383 }
384
385 #[test]
386 fn test_parse_int_error_wrapping() {
387 let inner_err = "not a number".parse::<i32>().unwrap_err();
389 let inner_str = inner_err.to_string();
391 let wrapped_err: Error = inner_err.into();
393 let wrapped_str = wrapped_err.to_string();
395 assert!(
397 wrapped_str.contains(&inner_str),
398 "Wrapped error '{}' should contain inner error '{}'",
399 wrapped_str,
400 inner_str
401 );
402 assert!(wrapped_str.starts_with("Integer parse error: "));
403 }
404
405 #[cfg(feature = "polars")]
406 #[test]
407 fn test_polars_error_wrapping() {
408 use polars::prelude::*;
410 let inner_err = DataFrame::new(vec![
411 Series::new("a".into(), &[1, 2, 3]).into(),
412 Series::new("a".into(), &[4, 5, 6]).into(),
413 ])
414 .unwrap_err();
415 let inner_str = inner_err.to_string();
417 let wrapped_err: Error = inner_err.into();
419 let wrapped_str = wrapped_err.to_string();
421 assert!(
423 wrapped_str.contains(&inner_str),
424 "Wrapped error '{}' should contain inner error '{}'",
425 wrapped_str,
426 inner_str
427 );
428 assert!(wrapped_str.starts_with("Polars error: "));
429 }
430
431 #[test]
439 fn test_max_retries_exceeded() {
440 let retry_count = 42u32;
442 let primitive_str = retry_count.to_string();
444 let wrapped_err = Error::MaxRetriesExceeded(retry_count);
446 let wrapped_str = wrapped_err.to_string();
448 assert!(
450 wrapped_str.contains(&primitive_str),
451 "Wrapped error '{}' should contain retry count '{}'",
452 wrapped_str,
453 primitive_str
454 );
455 assert!(wrapped_str.starts_with("Maximum retries"));
456 }
457
458 #[test]
459 fn test_rpc_error() {
460 let error_code = -32600;
462 let error_msg = "Invalid Request";
463 let code_str = error_code.to_string();
465 let wrapped_err = Error::RpcError(error_code, error_msg.to_string());
467 let wrapped_str = wrapped_err.to_string();
469 assert!(
471 wrapped_str.contains(&code_str),
472 "Wrapped error '{}' should contain error code '{}'",
473 wrapped_str,
474 code_str
475 );
476 assert!(
477 wrapped_str.contains(error_msg),
478 "Wrapped error '{}' should contain error message '{}'",
479 wrapped_str,
480 error_msg
481 );
482 assert!(wrapped_str.starts_with("RPC error"));
483 }
484
485 #[test]
486 fn test_invalid_rpc_id() {
487 let invalid_id = "not-a-valid-id-123";
489 let wrapped_err = Error::InvalidRpcId(invalid_id.to_string());
492 let wrapped_str = wrapped_err.to_string();
494 assert!(
496 wrapped_str.contains(invalid_id),
497 "Wrapped error '{}' should contain invalid ID '{}'",
498 wrapped_str,
499 invalid_id
500 );
501 assert!(wrapped_str.starts_with("Invalid RPC ID: "));
502 }
503
504 #[test]
505 fn test_invalid_file_type() {
506 let file_type = "unknown_format";
508 let wrapped_err = Error::InvalidFileType(file_type.to_string());
511 let wrapped_str = wrapped_err.to_string();
513 assert!(
515 wrapped_str.contains(file_type),
516 "Wrapped error '{}' should contain file type '{}'",
517 wrapped_str,
518 file_type
519 );
520 assert!(wrapped_str.starts_with("Invalid file type: "));
521 }
522
523 #[test]
524 fn test_invalid_annotation_type() {
525 let annotation_type = "unsupported_annotation";
527 let wrapped_err = Error::InvalidAnnotationType(annotation_type.to_string());
530 let wrapped_str = wrapped_err.to_string();
532 assert!(
534 wrapped_str.contains(annotation_type),
535 "Wrapped error '{}' should contain annotation type '{}'",
536 wrapped_str,
537 annotation_type
538 );
539 assert!(wrapped_str.starts_with("Invalid annotation type: "));
540 }
541
542 #[test]
543 fn test_unsupported_format() {
544 let format = "xyz_format";
546 let wrapped_err = Error::UnsupportedFormat(format.to_string());
549 let wrapped_str = wrapped_err.to_string();
551 assert!(
553 wrapped_str.contains(format),
554 "Wrapped error '{}' should contain format '{}'",
555 wrapped_str,
556 format
557 );
558 assert!(wrapped_str.starts_with("Unsupported format: "));
559 }
560
561 #[test]
562 fn test_missing_images() {
563 let details = "image001.jpg, image002.jpg";
565 let wrapped_err = Error::MissingImages(details.to_string());
568 let wrapped_str = wrapped_err.to_string();
570 assert!(
572 wrapped_str.contains(details),
573 "Wrapped error '{}' should contain details '{}'",
574 wrapped_str,
575 details
576 );
577 assert!(wrapped_str.starts_with("Missing images: "));
578 }
579
580 #[test]
581 fn test_missing_annotations() {
582 let details = "annotations.json";
584 let wrapped_err = Error::MissingAnnotations(details.to_string());
587 let wrapped_str = wrapped_err.to_string();
589 assert!(
591 wrapped_str.contains(details),
592 "Wrapped error '{}' should contain details '{}'",
593 wrapped_str,
594 details
595 );
596 assert!(wrapped_str.starts_with("Missing annotations: "));
597 }
598
599 #[test]
600 fn test_missing_label() {
601 let label = "person";
603 let wrapped_err = Error::MissingLabel(label.to_string());
606 let wrapped_str = wrapped_err.to_string();
608 assert!(
610 wrapped_str.contains(label),
611 "Wrapped error '{}' should contain label '{}'",
612 wrapped_str,
613 label
614 );
615 assert!(wrapped_str.starts_with("Missing label: "));
616 }
617
618 #[test]
619 fn test_invalid_parameters() {
620 let params = "batch_size must be positive";
622 let wrapped_err = Error::InvalidParameters(params.to_string());
625 let wrapped_str = wrapped_err.to_string();
627 assert!(
629 wrapped_str.contains(params),
630 "Wrapped error '{}' should contain params '{}'",
631 wrapped_str,
632 params
633 );
634 assert!(wrapped_str.starts_with("Invalid parameters: "));
635 }
636
637 #[test]
638 fn test_feature_not_enabled() {
639 let feature = "polars";
641 let wrapped_err = Error::FeatureNotEnabled(feature.to_string());
644 let wrapped_str = wrapped_err.to_string();
646 assert!(
648 wrapped_str.contains(feature),
649 "Wrapped error '{}' should contain feature '{}'",
650 wrapped_str,
651 feature
652 );
653 assert!(wrapped_str.starts_with("Feature not enabled: "));
654 }
655
656 #[test]
657 fn test_invalid_etag() {
658 let etag = "malformed-etag-value";
660 let wrapped_err = Error::InvalidEtag(etag.to_string());
663 let wrapped_str = wrapped_err.to_string();
665 assert!(
667 wrapped_str.contains(etag),
668 "Wrapped error '{}' should contain etag '{}'",
669 wrapped_str,
670 etag
671 );
672 assert!(wrapped_str.starts_with("Invalid ETag header: "));
673 }
674
675 #[test]
679 fn test_invalid_response() {
680 let err = Error::InvalidResponse;
681 let err_str = err.to_string();
682 assert_eq!(err_str, "Invalid server response");
683 }
684
685 #[test]
686 fn test_not_implemented() {
687 let err = Error::NotImplemented;
688 let err_str = err.to_string();
689 assert_eq!(err_str, "Not implemented");
690 }
691
692 #[test]
693 fn test_part_too_large() {
694 let err = Error::PartTooLarge;
695 let err_str = err.to_string();
696 assert_eq!(err_str, "File part size exceeds maximum limit");
697 }
698
699 #[test]
700 fn test_empty_token() {
701 let err = Error::EmptyToken;
702 let err_str = err.to_string();
703 assert_eq!(err_str, "Authentication token is empty");
704 }
705
706 #[test]
707 fn test_invalid_token() {
708 let err = Error::InvalidToken;
709 let err_str = err.to_string();
710 assert_eq!(err_str, "Invalid authentication token");
711 }
712
713 #[test]
714 fn test_token_expired() {
715 let err = Error::TokenExpired;
716 let err_str = err.to_string();
717 assert_eq!(err_str, "Authentication token has expired");
718 }
719
720 #[test]
721 fn test_unauthorized() {
722 let err = Error::Unauthorized;
723 let err_str = err.to_string();
724 assert_eq!(err_str, "Unauthorized access");
725 }
726}