youtube_uploader/
error.rs1#[derive(thiserror::Error, Debug)]
5#[non_exhaustive]
6pub enum UploadError {
7 #[error("HTTP request failed: {0}")]
8 Http(#[from] reqwest::Error),
9
10 #[error("I/O error: {0}")]
11 Io(#[from] std::io::Error),
12
13 #[error("Authentication failed: {0}")]
14 Auth(String),
15
16 #[error("Token refresh failed: {0}")]
17 TokenRefresh(String),
18
19 #[error("Platform API error ({status}): {message}")]
20 PlatformApi { status: u16, message: String },
21
22 #[error("Upload interrupted after {uploaded} of {total} bytes")]
23 Interrupted { uploaded: u64, total: u64 },
24
25 #[error("File too large: {size} bytes (max {max})")]
26 FileTooLarge { size: u64, max: u64 },
27
28 #[error("Unsupported file format: {0}")]
29 UnsupportedFormat(String),
30
31 #[error("Configuration error: {0}")]
32 Config(String),
33
34 #[error("Encryption error: {0}")]
35 Encryption(String),
36
37 #[error("Retry exhausted with no attempts made")]
38 NoAttempts,
39}
40
41impl UploadError {
42 pub fn is_retryable(&self) -> bool {
44 match self {
45 UploadError::Http(_) => true,
46 UploadError::Interrupted { .. } => true,
47 UploadError::PlatformApi { status, .. } => matches!(status, 500..=504 | 429),
48 _ => false,
49 }
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 #[test]
58 fn test_interrupted_error_is_retryable() {
59 let err = UploadError::Interrupted {
60 uploaded: 1000,
61 total: 5000,
62 };
63 assert!(err.is_retryable());
64 }
65
66 #[test]
67 fn test_platform_api_5xx_is_retryable() {
68 let err = UploadError::PlatformApi {
69 status: 500,
70 message: "Server error".into(),
71 };
72 assert!(err.is_retryable());
73
74 let err = UploadError::PlatformApi {
75 status: 502,
76 message: "Bad gateway".into(),
77 };
78 assert!(err.is_retryable());
79
80 let err = UploadError::PlatformApi {
81 status: 503,
82 message: "Service unavailable".into(),
83 };
84 assert!(err.is_retryable());
85
86 let err = UploadError::PlatformApi {
87 status: 429,
88 message: "Rate limited".into(),
89 };
90 assert!(err.is_retryable());
91 }
92
93 #[test]
94 fn test_platform_api_4xx_not_retryable() {
95 let err = UploadError::PlatformApi {
96 status: 400,
97 message: "Bad request".into(),
98 };
99 assert!(!err.is_retryable());
100
101 let err = UploadError::PlatformApi {
102 status: 401,
103 message: "Unauthorized".into(),
104 };
105 assert!(!err.is_retryable());
106
107 let err = UploadError::PlatformApi {
108 status: 404,
109 message: "Not found".into(),
110 };
111 assert!(!err.is_retryable());
112 }
113
114 #[test]
115 fn test_auth_error_not_retryable() {
116 let err = UploadError::Auth("Invalid credentials".into());
117 assert!(!err.is_retryable());
118 }
119
120 #[test]
121 fn test_io_error_not_retryable() {
122 let err = UploadError::Io(std::io::Error::new(
123 std::io::ErrorKind::NotFound,
124 "File not found",
125 ));
126 assert!(!err.is_retryable());
127 }
128
129 #[test]
130 fn test_encryption_error_not_retryable() {
131 let err = UploadError::Encryption("failed to decrypt".into());
132 assert!(!err.is_retryable());
133 }
134
135 #[test]
136 fn test_no_attempts_error_not_retryable() {
137 let err = UploadError::NoAttempts;
138 assert!(!err.is_retryable());
139 }
140}