@@ -13,31 +13,41 @@ const RETRY_DELAY_MS: u64 = 1000;
1313
1414#[ derive( Error , Debug , Clone ) ]
1515pub enum ApiError {
16- #[ error( "Request failed : {0}" ) ]
17- RequestError ( String ) ,
16+ #[ error( "Network error : {0}" ) ]
17+ NetworkError ( String ) ,
1818
19- #[ error( "Not found: {0}" ) ]
19+ #[ error( "HTTP {0}: {1}" ) ]
20+ HttpError ( u16 , String ) ,
21+
22+ #[ error( "HTTP 404: Resource not found" ) ]
2023 NotFound ( String ) ,
2124
22- #[ error( "Unauthorized : Please check your authentication credentials" ) ]
25+ #[ error( "HTTP 401 : Please check your authentication credentials" ) ]
2326 Unauthorized ,
2427
25- #[ error( "Token expired" ) ]
28+ #[ error( "HTTP 401: Token expired" ) ]
2629 TokenExpired ,
2730
28- #[ error( "Server timeout - please retry" ) ]
29- Timeout ,
31+ #[ error( "HTTP {0}: Server timeout" ) ]
32+ Timeout ( u16 ) ,
33+
34+ #[ error( "HTTP {0}: {1}" ) ]
35+ ServerError ( u16 , String ) ,
3036
31- #[ error( "Server error: {0}" ) ]
32- ServerError ( String ) ,
37+ #[ error( "{0}" ) ]
38+ InternalError ( String ) ,
3339}
3440
3541impl From < reqwest:: Error > for ApiError {
3642 fn from ( e : reqwest:: Error ) -> Self {
3743 if e. is_timeout ( ) {
38- ApiError :: Timeout
44+ ApiError :: Timeout ( 0 ) // 0 indicates network-level timeout (no HTTP response)
45+ } else if e. is_connect ( ) {
46+ ApiError :: NetworkError ( format ! ( "Connection failed: {}" , e) )
47+ } else if e. is_request ( ) {
48+ ApiError :: NetworkError ( format ! ( "Request error: {}" , e) )
3949 } else {
40- ApiError :: RequestError ( e. to_string ( ) )
50+ ApiError :: NetworkError ( e. to_string ( ) )
4151 }
4252 }
4353}
@@ -158,7 +168,7 @@ impl ApiClient {
158168 F : Fn ( ) -> Fut ,
159169 Fut : std:: future:: Future < Output = Result < String , ApiError > > ,
160170 {
161- let mut last_error = ApiError :: RequestError ( "No attempts made" . to_string ( ) ) ;
171+ let mut last_error = ApiError :: NetworkError ( "No attempts made" . to_string ( ) ) ;
162172 let mut token_refreshed = false ;
163173
164174 for attempt in 0 ..MAX_RETRIES {
@@ -174,18 +184,21 @@ impl ApiClient {
174184 }
175185 return Err ( ApiError :: Unauthorized ) ;
176186 }
177- Err ( ApiError :: Timeout ) | Err ( ApiError :: ServerError ( _) )
187+ Err ( ref e @ ApiError :: Timeout ( _) )
188+ | Err ( ref e @ ApiError :: ServerError ( _, _) )
189+ | Err ( ref e @ ApiError :: NetworkError ( _) )
178190 if attempt < MAX_RETRIES - 1 =>
179191 {
180192 let delay = RETRY_DELAY_MS * ( attempt as u64 + 1 ) ;
181193 eprintln ! (
182- "Request failed, retrying in {}ms... (attempt {}/{})" ,
194+ "{}, retrying in {}ms... (attempt {}/{})" ,
195+ e,
183196 delay,
184197 attempt + 1 ,
185198 MAX_RETRIES
186199 ) ;
187200 sleep ( Duration :: from_millis ( delay) ) . await ;
188- last_error = ApiError :: Timeout ;
201+ last_error = e . clone ( ) ;
189202 }
190203 Err ( e) => return Err ( e) ,
191204 }
@@ -196,6 +209,7 @@ impl ApiClient {
196209
197210 async fn handle_response ( & self , response : reqwest:: Response ) -> Result < String , ApiError > {
198211 let status = response. status ( ) ;
212+ let status_code = status. as_u16 ( ) ;
199213
200214 if status. is_success ( ) {
201215 Ok ( response. text ( ) . await ?)
@@ -206,10 +220,14 @@ impl ApiClient {
206220 } else if status == StatusCode :: FORBIDDEN {
207221 Err ( ApiError :: Unauthorized )
208222 } else if status == StatusCode :: GATEWAY_TIMEOUT || status == StatusCode :: REQUEST_TIMEOUT {
209- Err ( ApiError :: Timeout )
223+ Err ( ApiError :: Timeout ( status_code ) )
210224 } else {
211225 let body = response. text ( ) . await . unwrap_or_default ( ) ;
212- Err ( ApiError :: ServerError ( format ! ( "HTTP {}: {}" , status, body) ) )
226+ if status. is_server_error ( ) {
227+ Err ( ApiError :: ServerError ( status_code, body) )
228+ } else {
229+ Err ( ApiError :: HttpError ( status_code, body) )
230+ }
213231 }
214232 }
215233}
0 commit comments