From 20602eb863d10bd5ee6dc2f499a681fb835980d0 Mon Sep 17 00:00:00 2001 From: N08I40K Date: Fri, 18 Apr 2025 00:11:05 +0400 Subject: [PATCH] =?UTF-8?q?=D0=A3=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD?= =?UTF-8?q?=D0=BD=D0=BE=D0=B5=20=D0=BE=D1=82=D0=BE=D0=B1=D1=80=D0=B0=D0=B6?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BE=D0=BA?= =?UTF-8?q?=20=D0=BF=D1=80=D0=B8=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B8=20=D1=81=D1=81=D1=8B=D0=BB=D0=BA=D0=B8?= =?UTF-8?q?=20=D1=80=D0=B0=D1=81=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D0=B8=D1=8F?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/schedule/update_download_url.rs | 21 +++++----- src/xls_downloader/basic_impl.rs | 49 ++++++++++------------ src/xls_downloader/interface.rs | 19 ++++++--- 3 files changed, 48 insertions(+), 41 deletions(-) diff --git a/src/routes/schedule/update_download_url.rs b/src/routes/schedule/update_download_url.rs index 46b17c8..4bb8e48 100644 --- a/src/routes/schedule/update_download_url.rs +++ b/src/routes/schedule/update_download_url.rs @@ -60,20 +60,20 @@ pub async fn update_download_url( } }, Err(error) => { - if let FetchError::Unknown(error) = error { + if let FetchError::Unknown(error) = &error { sentry::capture_error(&error); } - ErrorCode::DownloadFailed.into_response() + ErrorCode::DownloadFailed(error).into_response() } } } Err(error) => { - if let FetchError::Unknown(error) = error { + if let FetchError::Unknown(error) = &error { sentry::capture_error(&error); } - ErrorCode::FetchFailed.into_response() + ErrorCode::FetchFailed(error).into_response() } } } @@ -85,6 +85,7 @@ mod schema { use derive_more::Display; use serde::{Deserialize, Serialize, Serializer}; use utoipa::ToSchema; + use crate::xls_downloader::interface::FetchError; pub type ServiceResponse = crate::routes::schema::Response; @@ -103,12 +104,12 @@ mod schema { NonWhitelistedHost, /// Failed to retrieve file metadata. - #[display("Unable to retrieve metadata from the specified URL.")] - FetchFailed, + #[display("Unable to retrieve metadata from the specified URL: {_0}")] + FetchFailed(FetchError), /// Failed to download the file. - #[display("Unable to retrieve data from the specified URL.")] - DownloadFailed, + #[display("Unable to retrieve data from the specified URL: {_0}")] + DownloadFailed(FetchError), /// The link leads to an outdated schedule. /// @@ -129,8 +130,8 @@ mod schema { { match self { ErrorCode::NonWhitelistedHost => serializer.serialize_str("NON_WHITELISTED_HOST"), - ErrorCode::FetchFailed => serializer.serialize_str("FETCH_FAILED"), - ErrorCode::DownloadFailed => serializer.serialize_str("DOWNLOAD_FAILED"), + ErrorCode::FetchFailed(_) => serializer.serialize_str("FETCH_FAILED"), + ErrorCode::DownloadFailed(_) => serializer.serialize_str("DOWNLOAD_FAILED"), ErrorCode::OutdatedSchedule => serializer.serialize_str("OUTDATED_SCHEDULE"), ErrorCode::InvalidSchedule(_) => serializer.serialize_str("INVALID_SCHEDULE"), } diff --git a/src/xls_downloader/basic_impl.rs b/src/xls_downloader/basic_impl.rs index c58e627..f3f1a74 100644 --- a/src/xls_downloader/basic_impl.rs +++ b/src/xls_downloader/basic_impl.rs @@ -1,6 +1,7 @@ use crate::xls_downloader::interface::{FetchError, FetchOk, FetchResult, XLSDownloader}; use chrono::{DateTime, Utc}; use std::env; +use std::sync::Arc; pub struct BasicXlsDownloader { pub url: Option, @@ -22,7 +23,7 @@ async fn fetch_specified(url: &String, user_agent: &String, head: bool) -> Fetch match response { Ok(r) => { if r.status().as_u16() != 200 { - return Err(FetchError::BadStatusCode); + return Err(FetchError::BadStatusCode(r.status().as_u16())); } let headers = r.headers(); @@ -32,11 +33,18 @@ async fn fetch_specified(url: &String, user_agent: &String, head: bool) -> Fetch let last_modified = headers.get("last-modified"); let date = headers.get("date"); - if content_type.is_none() || etag.is_none() || last_modified.is_none() || date.is_none() - { - Err(FetchError::BadHeaders) + if content_type.is_none() { + Err(FetchError::BadHeaders("Content-Type".to_string())) + } else if etag.is_none() { + Err(FetchError::BadHeaders("ETag".to_string())) + } else if last_modified.is_none() { + Err(FetchError::BadHeaders("Last-Modified".to_string())) + } else if date.is_none() { + Err(FetchError::BadHeaders("Date".to_string())) } else if content_type.unwrap() != "application/vnd.ms-excel" { - Err(FetchError::BadContentType) + Err(FetchError::BadContentType( + content_type.unwrap().to_str().unwrap().to_string(), + )) } else { let etag = etag.unwrap().to_str().unwrap().to_string(); let last_modified = @@ -51,7 +59,7 @@ async fn fetch_specified(url: &String, user_agent: &String, head: bool) -> Fetch }) } } - Err(e) => Err(FetchError::Unknown(e)), + Err(error) => Err(FetchError::Unknown(Arc::new(error))), } } @@ -86,7 +94,7 @@ impl XLSDownloader for BasicXlsDownloader { #[cfg(test)] mod tests { - use crate::xls_downloader::basic_impl::{BasicXlsDownloader, fetch_specified}; + use crate::xls_downloader::basic_impl::{fetch_specified, BasicXlsDownloader}; use crate::xls_downloader::interface::{FetchError, XLSDownloader}; #[tokio::test] @@ -116,14 +124,10 @@ mod tests { assert!(results[0].is_err()); assert!(results[1].is_err()); - assert_eq!( - *results[0].as_ref().err().unwrap(), - FetchError::BadStatusCode - ); - assert_eq!( - *results[1].as_ref().err().unwrap(), - FetchError::BadStatusCode - ); + let expected_error = FetchError::BadStatusCode(404); + + assert_eq!(*results[0].as_ref().err().unwrap(), expected_error); + assert_eq!(*results[1].as_ref().err().unwrap(), expected_error); } #[tokio::test] @@ -139,8 +143,10 @@ mod tests { assert!(results[0].is_err()); assert!(results[1].is_err()); - assert_eq!(*results[0].as_ref().err().unwrap(), FetchError::BadHeaders); - assert_eq!(*results[1].as_ref().err().unwrap(), FetchError::BadHeaders); + let expected_error = FetchError::BadHeaders("ETag".to_string()); + + assert_eq!(*results[0].as_ref().err().unwrap(), expected_error); + assert_eq!(*results[1].as_ref().err().unwrap(), expected_error); } #[tokio::test] @@ -155,15 +161,6 @@ mod tests { assert!(results[0].is_err()); assert!(results[1].is_err()); - - assert_eq!( - *results[0].as_ref().err().unwrap(), - FetchError::BadContentType - ); - assert_eq!( - *results[1].as_ref().err().unwrap(), - FetchError::BadContentType - ); } #[tokio::test] diff --git a/src/xls_downloader/interface.rs b/src/xls_downloader/interface.rs index b14a09c..10dca0f 100644 --- a/src/xls_downloader/interface.rs +++ b/src/xls_downloader/interface.rs @@ -1,23 +1,32 @@ use chrono::{DateTime, Utc}; +use derive_more::Display; use std::mem::discriminant; +use std::sync::Arc; +use utoipa::ToSchema; /// XLS data retrieval errors. -#[derive(Debug)] +#[derive(Clone, Debug, ToSchema, Display)] pub enum FetchError { /// File url is not set. + #[display("The link to the timetable was not provided earlier.")] NoUrlProvided, /// Unknown error. - Unknown(reqwest::Error), + #[display("An unknown error occurred while downloading the file.")] + #[schema(value_type = String)] + Unknown(Arc), /// Server returned a status code different from 200. - BadStatusCode, + #[display("Server returned a status code {_0}.")] + BadStatusCode(u16), /// The url leads to a file of a different type. - BadContentType, + #[display("The link leads to a file of type '{_0}'.")] + BadContentType(String), /// Server doesn't return expected headers. - BadHeaders, + #[display("Server doesn't return expected header(s) '{_0}'.")] + BadHeaders(String), } impl PartialEq for FetchError {