From b685c3a8312b3bfd80aa5dc32bb38728e35784ef Mon Sep 17 00:00:00 2001 From: Vegard Matthey Date: Wed, 25 Jun 2025 00:35:35 +0200 Subject: [PATCH] impl TryFrom instead of having conversion functions --- Cargo.toml | 6 +- src/main.rs | 4 +- src/response_deserializer.rs | 748 ++++++++++++++++++----------------- src/select.rs | 4 +- src/util.rs | 8 +- 5 files changed, 396 insertions(+), 374 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ae0d219..1f47100 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,13 +5,13 @@ edition = "2021" [dependencies] chrono = "0.4.38" -crossterm = "0.28.1" +crossterm = "0.29" futures = "0.3.30" icy_sixel = "0.1.2" image = { version = "0.25.2", default-features = false, features = ["jpeg", "png"] } reqwest = "0.12.5" -reqwest-middleware = "0.3.2" -reqwest-retry = "0.6.0" +reqwest-middleware = "0.4" +reqwest-retry = "0.7" serde = { version = "1.0.204", features = ["derive"] } serde_json = "1.0.121" tokio = { version = "1.39.2", default-features = false, features = ["macros", "rt-multi-thread"] } diff --git a/src/main.rs b/src/main.rs index 4bcc5cd..b136c8a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -149,7 +149,7 @@ async fn main() { let mut chapters = match get_chapters(client, &manga.id).await { Ok(v) => v, Err(e) => { - eprintln!("ERROR: {:#?}", e); + eprintln!("ERROR: {e:#?}"); std::process::exit(1); } }; @@ -458,7 +458,7 @@ async fn select_manga_from_search( let choice = match select::select(&entries) { Ok(v) => v, Err(e) => { - eprintln!("ERROR: Failed to select: {:?}", e); + eprintln!("ERROR: Failed to select: {e:?}"); std::process::exit(1); } }; diff --git a/src/response_deserializer.rs b/src/response_deserializer.rs index de27706..eccf81d 100644 --- a/src/response_deserializer.rs +++ b/src/response_deserializer.rs @@ -625,21 +625,21 @@ enum ChapterImageError { Result(String), } -impl TryInto for &str { +impl TryFrom<&str> for State { type Error = (); - fn try_into(self) -> Result { - Ok(match self { + fn try_from(s: &str) -> Result { + Ok(match s { "published" => State::Published, _ => return Err(()), }) } } -impl TryInto for &str { +impl TryFrom<&str> for ContentRating { type Error = (); - fn try_into(self) -> Result { - Ok(match self { + fn try_from(s: &str) -> Result { + Ok(match s { "safe" => ContentRating::Safe, "suggestive" => ContentRating::Suggestive, "erotica" => ContentRating::Erotica, @@ -648,11 +648,11 @@ impl TryInto for &str { }) } } -impl TryInto for &str { +impl TryFrom<&str> for Language { type Error = (); - fn try_into(self) -> Result { - Ok(match self { + fn try_from(s: &str) -> Result { + Ok(match s { "ab" => Language::Abkhazian, "aa" => Language::Afar, "af" => Language::Afrikaans, @@ -883,11 +883,11 @@ impl TryInto for &str { }) } } -impl TryInto for &str { +impl TryFrom<&str> for PublicationDemographic { type Error = (); - fn try_into(self) -> Result { - Ok(match self { + fn try_from(s: &str) -> Result { + Ok(match s { "shounen" => PublicationDemographic::Shounen, "josei" => PublicationDemographic::Josei, "shoujo" => PublicationDemographic::Shoujo, @@ -896,11 +896,11 @@ impl TryInto for &str { }) } } -impl TryInto for &str { +impl TryFrom<&str> for DataType { type Error = (); - fn try_into(self) -> Result { - Ok(match self { + fn try_from(s: &str) -> Result { + Ok(match s { "manga" => DataType::Manga, "chapter" => DataType::Chapter, "cover_art" => DataType::CoverArt, @@ -915,11 +915,11 @@ impl TryInto for &str { }) } } -impl TryInto for &str { +impl TryFrom<&str> for Status { type Error = (); - fn try_into(self) -> Result { - Ok(match self { + fn try_from(s: &str) -> Result { + Ok(match s { "ongoing" => Status::Ongoing, "completed" => Status::Completed, "hiatus" => Status::Hiatus, @@ -928,21 +928,21 @@ impl TryInto for &str { }) } } -impl TryInto for &str { +impl TryFrom<&str> for ResponseResult { type Error = (); - fn try_into(self) -> Result { - match self { + fn try_from(s: &str) -> Result { + match s { "ok" => Ok(ResponseResult::Ok), _ => Err(()), } } } -impl TryInto for &str { +impl TryFrom<&str> for Response { type Error = (); - fn try_into(self) -> Result { - match self { + fn try_from(s: &str) -> Result { + match s { "collection" => Ok(Response::Collection), "entity" => Ok(Response::Entity), _ => Err(()), @@ -950,34 +950,35 @@ impl TryInto for &str { } } -fn convert_response_to_result( - search_response: SearchResponse, -) -> Result { - let response = (search_response.response.as_str()) - .try_into() - .map_err(|_| ResponseConversionError::Result(search_response.response))?; - let result: ResponseResult = (search_response.result.as_str()) - .try_into() - .map_err(|_| ResponseConversionError::Result(search_response.result))?; +impl TryFrom for SearchResult { + type Error = ResponseConversionError; + fn try_from(search_response: SearchResponse) -> Result { + let response = (search_response.response.as_str()) + .try_into() + .map_err(|_| ResponseConversionError::Result(search_response.response))?; + let result: ResponseResult = (search_response.result.as_str()) + .try_into() + .map_err(|_| ResponseConversionError::Result(search_response.result))?; - let mut data: Result, ResponseConversionError> = - Ok(Vec::with_capacity(search_response.data.len())); - for m in search_response.data { - if let Ok(ref mut d) = data { - match convert_data_to_manga(m) { - Ok(v) => d.push(v), - Err(e) => { - data = Err(e); - break; + let mut data: Result, ResponseConversionError> = + Ok(Vec::with_capacity(search_response.data.len())); + for m in search_response.data { + if let Ok(ref mut d) = data { + match m.try_into() { + Ok(v) => d.push(v), + Err(e) => { + data = Err(e); + break; + } } } } + Ok(SearchResult { + response, + result, + data: data?, + }) } - Ok(SearchResult { - response, - result, - data: data?, - }) } #[derive(Debug)] @@ -1023,192 +1024,198 @@ impl Display for ContentRating { } } -fn convert_attributes( - attributes: ContentAttributes, -) -> Result { - Ok(MangaAttributes { - title: attributes.title, - alt_titles: attributes.alt_titles, - description: attributes.description, - is_locked: attributes.is_locked, - links: attributes.links, - original_language: (attributes.original_language.as_str()) - .try_into() - .map_err(|_| AttributeConversionError::Language(attributes.original_language))?, - last_volume: match attributes.last_volume { - Some(s) => match s.parse() { - Ok(v) => Some(v), - Err(_) => { - if s.is_empty() { - None - } else { - return Err(AttributeConversionError::LastVolume(s)); +impl TryFrom for MangaAttributes { + type Error = AttributeConversionError; + fn try_from(attributes: ContentAttributes) -> Result { + Ok(MangaAttributes { + title: attributes.title, + alt_titles: attributes.alt_titles, + description: attributes.description, + is_locked: attributes.is_locked, + links: attributes.links, + original_language: (attributes.original_language.as_str()) + .try_into() + .map_err(|_| AttributeConversionError::Language(attributes.original_language))?, + last_volume: match attributes.last_volume { + Some(s) => match s.parse() { + Ok(v) => Some(v), + Err(_) => { + if s.is_empty() { + None + } else { + return Err(AttributeConversionError::LastVolume(s)); + } } - } + }, + None => None, }, - None => None, - }, - last_chapter: match attributes.last_chapter { - Some(n) => match n.parse() { - Ok(v) => Some(v), - Err(_) => { - if n.is_empty() { - None - } else { - return Err(AttributeConversionError::LastVolume(n)); + last_chapter: match attributes.last_chapter { + Some(n) => match n.parse() { + Ok(v) => Some(v), + Err(_) => { + if n.is_empty() { + None + } else { + return Err(AttributeConversionError::LastVolume(n)); + } } - } + }, + None => None, }, - None => None, - }, - publication_demographic: match attributes.publication_demographic { - Some(s) => Some( - (s.as_str()) - .try_into() - .map_err(|_| AttributeConversionError::PublicationDemographic(s))?, - ), - None => None, - }, - status: (attributes.status.as_str()) - .try_into() - .map_err(|_| AttributeConversionError::Status(attributes.status))?, - year: attributes.year, - content_rating: attributes - .content_rating - .as_str() - .try_into() - .map_err(|_| AttributeConversionError::ContentRating(attributes.content_rating))?, - tags: { - let mut tags = Vec::with_capacity(attributes.tags.len()); - for m in attributes.tags { - tags.push(({ - || { - Ok::(Tag { - data_type: (m.type_name.as_str()) - .try_into() - .map_err(|_| AttributeConversionError::DataType(m.type_name))?, - id: Id(m.id), - relationships: { - let mut relationships = Vec::with_capacity(m.relationships.len()); - for m in m.relationships { - relationships.push(({ - || { - Ok::( - Relationship { - id: Id(m.id), - data_type: (m.type_name.as_str()) - .try_into() - .map_err(|_| { - AttributeConversionError::DataType( - m.type_name, - ) - })?, - // TODO: Do this - attributes: None, - related: m.related, - }, - ) - } - })()?); - } - relationships - }, - attributes: TagAttributes { - name: m.attributes.name, - group: m.attributes.group, - version: m.attributes.version, - description: Description { - en: m.attributes.description.en, - ru: m.attributes.description.ru, - }, - }, - }) - } - })()?); - } - tags - }, - state: (attributes.state.as_str()) - .try_into() - .map_err(|_| AttributeConversionError::State(attributes.state))?, - chapter_numbers_reset_on_new_volume: attributes.chapter_numbers_reset_on_new_volume, - created_at: DateTime::parse_from_rfc3339(&attributes.created_at).map_err(|_| { - AttributeConversionError::CreatedAtDateTime(attributes.created_at.clone()) - })?, - updated_at: DateTime::parse_from_rfc3339(&attributes.updated_at) - .map_err(|_| AttributeConversionError::UpdatedAtDateTime(attributes.created_at))?, - version: attributes.version, - available_translated_languages: { - let mut av = Vec::with_capacity(attributes.available_translated_languages.len()); - for m in attributes.available_translated_languages { - av.push(({ - || { - Ok::, AttributeConversionError>(match m { - Some(s) => Some( - (s.as_str()) + publication_demographic: match attributes.publication_demographic { + Some(s) => Some( + (s.as_str()) + .try_into() + .map_err(|_| AttributeConversionError::PublicationDemographic(s))?, + ), + None => None, + }, + status: (attributes.status.as_str()) + .try_into() + .map_err(|_| AttributeConversionError::Status(attributes.status))?, + year: attributes.year, + content_rating: attributes + .content_rating + .as_str() + .try_into() + .map_err(|_| AttributeConversionError::ContentRating(attributes.content_rating))?, + tags: { + let mut tags = Vec::with_capacity(attributes.tags.len()); + for m in attributes.tags { + tags.push(({ + || { + Ok::(Tag { + data_type: (m.type_name.as_str()) .try_into() - .map_err(|_| AttributeConversionError::Language(s))?, - ), - None => None, - }) - } - })()?); - } - av - }, - latest_uploaded_chapter: attributes - .latest_uploaded_chapter - .as_ref() - .map(|m| Id(m.clone())), - }) + .map_err(|_| AttributeConversionError::DataType(m.type_name))?, + id: Id(m.id), + relationships: { + let mut relationships = + Vec::with_capacity(m.relationships.len()); + for m in m.relationships { + relationships.push(({ + || { + Ok::( + Relationship { + id: Id(m.id), + data_type: (m.type_name.as_str()) + .try_into() + .map_err(|_| { + AttributeConversionError::DataType( + m.type_name, + ) + })?, + // TODO: Do this + attributes: None, + related: m.related, + }, + ) + } + })( + )?); + } + relationships + }, + attributes: TagAttributes { + name: m.attributes.name, + group: m.attributes.group, + version: m.attributes.version, + description: Description { + en: m.attributes.description.en, + ru: m.attributes.description.ru, + }, + }, + }) + } + })()?); + } + tags + }, + state: (attributes.state.as_str()) + .try_into() + .map_err(|_| AttributeConversionError::State(attributes.state))?, + chapter_numbers_reset_on_new_volume: attributes.chapter_numbers_reset_on_new_volume, + created_at: DateTime::parse_from_rfc3339(&attributes.created_at).map_err(|_| { + AttributeConversionError::CreatedAtDateTime(attributes.created_at.clone()) + })?, + updated_at: DateTime::parse_from_rfc3339(&attributes.updated_at) + .map_err(|_| AttributeConversionError::UpdatedAtDateTime(attributes.created_at))?, + version: attributes.version, + available_translated_languages: { + let mut av = Vec::with_capacity(attributes.available_translated_languages.len()); + for m in attributes.available_translated_languages { + av.push(({ + || { + Ok::, AttributeConversionError>(match m { + Some(s) => Some( + (s.as_str()) + .try_into() + .map_err(|_| AttributeConversionError::Language(s))?, + ), + None => None, + }) + } + })()?); + } + av + }, + latest_uploaded_chapter: attributes + .latest_uploaded_chapter + .as_ref() + .map(|m| Id(m.clone())), + }) + } } pub fn deserialize_id_query(json: &str) -> IdQueryResult { let id_query_response: IdQueryResponse = match serde_json::from_str(json) { Ok(v) => v, Err(e) => { - eprintln!("ERROR: {:#?}", e); + eprintln!("ERROR: {e:#?}"); std::fs::write("out.json", json).unwrap(); std::process::exit(1); } }; - convert_id_query(id_query_response).unwrap() + id_query_response.try_into().unwrap() } -fn convert_id_query(response: IdQueryResponse) -> Result { - Ok(IdQueryResult { - result: response.result.as_str().try_into().unwrap(), - response: response.response.as_str().try_into().unwrap(), - data: convert_data_to_manga(response.data).unwrap(), - }) +impl TryFrom for IdQueryResult { + type Error = AttributeConversionError; + fn try_from(response: IdQueryResponse) -> Result { + Ok(IdQueryResult { + result: response.result.as_str().try_into().unwrap(), + response: response.response.as_str().try_into().unwrap(), + data: response.data.try_into().unwrap(), + }) + } } pub fn deserialize_chapter_feed(json: &str) -> ChapterFeed { let chapter_feed_response: ChapterFeedResponse = match serde_json::from_str(json) { Ok(v) => v, Err(e) => { - eprintln!("ERROR: {:#?}", e); + eprintln!("ERROR: {e:#?}"); std::fs::write("chapter_feed.json", json).unwrap(); std::process::exit(1); } }; - convert_chapter_feed(chapter_feed_response).unwrap() + chapter_feed_response.try_into().unwrap() } pub fn deserializer(json: &str) -> SearchResult { let search_response: SearchResponse = match serde_json::from_str(json) { Ok(v) => v, Err(e) => { - eprintln!("ERROR: {:#?}", e); + eprintln!("ERROR: {e:#?}"); std::fs::write("search_result.json", json).unwrap(); std::process::exit(1); } }; - let search_result = convert_response_to_result(search_response); + let search_result = search_response.try_into(); match search_result { Ok(v) => v, Err(e) => { - eprintln!("ERROR: Failed to convert search response: {:#?}", e); + eprintln!("ERROR: Failed to convert search response: {e:#?}"); std::process::exit(1); } } @@ -1235,66 +1242,70 @@ enum ChapterRelationshipError { Id(String), } -fn convert_chapter_feed( - feed: ChapterFeedResponse, -) -> Result { - // Now this is a bit of an abomination. It uses closures such that you can use the ? syntax - // sugar to return an error. Also uses for loops instead of iterators since they do not take - // ownership. I think I should have just kept the iterators. - let mut data: Vec = Vec::with_capacity(feed.data.len()); - for m in feed.data { - let chapter: Chapter = ({ - || { - Ok::(Chapter { - data_type: (m.type_name.as_str()) - .try_into() - .map_err(|_| ChapterConversionError::DataType(m.type_name))?, - id: Id(m.id), - attributes: convert_chapter_attributes(m.attributes) - .map_err(ChapterConversionError::Attributes)?, - relationships: { - let mut relationships = Vec::with_capacity(m.relationships.len()); - for r in m.relationships { - relationships.push( - ({ - || { - Ok({ - ChapterRelationship { - data_type: (r.type_name.as_str()) - .try_into() - .map_err(|_| { - ChapterRelationshipError::TypeData( - r.type_name, - ) - })?, - id: Id(r.id), - } - }) - } - })() - .map_err(ChapterConversionError::Relationship)?, - ); - } - relationships - }, - }) - } - })() - .map_err(ChapterFeedConversionError::Chapter)?; - data.push(chapter); +impl TryFrom for ChapterFeed { + type Error = ChapterFeedConversionError; + + fn try_from(feed: ChapterFeedResponse) -> Result { + // Now this is a bit of an abomination. It uses closures such that you can use the ? syntax + // sugar to return an error. Also uses for loops instead of iterators since they do not take + // ownership. I think I should have just kept the iterators. + let mut data: Vec = Vec::with_capacity(feed.data.len()); + for m in feed.data { + let chapter: Chapter = ({ + || { + Ok::(Chapter { + data_type: (m.type_name.as_str()) + .try_into() + .map_err(|_| ChapterConversionError::DataType(m.type_name))?, + id: Id(m.id), + attributes: m + .attributes + .try_into() + .map_err(ChapterConversionError::Attributes)?, + relationships: { + let mut relationships = Vec::with_capacity(m.relationships.len()); + for r in m.relationships { + relationships.push( + ({ + || { + Ok({ + ChapterRelationship { + data_type: (r.type_name.as_str()) + .try_into() + .map_err(|_| { + ChapterRelationshipError::TypeData( + r.type_name, + ) + })?, + id: Id(r.id), + } + }) + } + })() + .map_err(ChapterConversionError::Relationship)?, + ); + } + relationships + }, + }) + } + })() + .map_err(ChapterFeedConversionError::Chapter)?; + data.push(chapter); + } + Ok(ChapterFeed { + result: (feed.result.as_str()) + .try_into() + .map_err(|_| ChapterFeedConversionError::Result(feed.result))?, + response: (feed.response.as_str()) + .try_into() + .map_err(|_| ChapterFeedConversionError::Result(feed.response))?, + data, + limit: feed.limit, + offset: feed.offset, + total: feed.total, + }) } - Ok(ChapterFeed { - result: (feed.result.as_str()) - .try_into() - .map_err(|_| ChapterFeedConversionError::Result(feed.result))?, - response: (feed.response.as_str()) - .try_into() - .map_err(|_| ChapterFeedConversionError::Result(feed.response))?, - data, - limit: feed.limit, - offset: feed.offset, - total: feed.total, - }) } #[derive(Debug)] @@ -1307,53 +1318,59 @@ enum ChapterAttributeConversionError { TranslatedLanguage(String), } -fn convert_chapter_attributes( - attributes: ChapterAttributesContent, -) -> Result { - Ok(ChapterAttributes { - volume: match &attributes.volume { - Some(v) => match v.parse() { - Ok(n) => Some(n), - Err(_) => return Err(ChapterAttributeConversionError::Volume(v.to_owned())), +impl TryFrom for ChapterAttributes { + type Error = ChapterAttributeConversionError; + fn try_from(attributes: ChapterAttributesContent) -> Result { + Ok(ChapterAttributes { + volume: match &attributes.volume { + Some(v) => match v.parse() { + Ok(n) => Some(n), + Err(_) => return Err(ChapterAttributeConversionError::Volume(v.to_owned())), + }, + None => None, }, - None => None, - }, - chapter: match &attributes.chapter { - Some(v) => match v.parse() { - Ok(v) => Some(v), - Err(_) => return Err(ChapterAttributeConversionError::Chapter(v.to_owned())), + chapter: match &attributes.chapter { + Some(v) => match v.parse() { + Ok(v) => Some(v), + Err(_) => return Err(ChapterAttributeConversionError::Chapter(v.to_owned())), + }, + None => None, }, - None => None, - }, - created_at: DateTime::parse_from_rfc3339(&attributes.created_at) - .map_err(|_| ChapterAttributeConversionError::CreatedAt(attributes.created_at))?, - published_at: DateTime::parse_from_rfc3339(&attributes.publish_at) - .map_err(|_| ChapterAttributeConversionError::CreatedAt(attributes.publish_at))?, - updated_at: DateTime::parse_from_rfc3339(&attributes.updated_at) - .map_err(|_| ChapterAttributeConversionError::CreatedAt(attributes.updated_at))?, - external_url: attributes.external_url, - title: attributes.title, - pages: attributes.pages, - translated_language: (attributes.translated_language.as_str()) - .try_into() - .map_err(|_| { - ChapterAttributeConversionError::TranslatedLanguage(attributes.translated_language) - })?, - version: attributes.version, - }) + created_at: DateTime::parse_from_rfc3339(&attributes.created_at) + .map_err(|_| ChapterAttributeConversionError::CreatedAt(attributes.created_at))?, + published_at: DateTime::parse_from_rfc3339(&attributes.publish_at) + .map_err(|_| ChapterAttributeConversionError::CreatedAt(attributes.publish_at))?, + updated_at: DateTime::parse_from_rfc3339(&attributes.updated_at) + .map_err(|_| ChapterAttributeConversionError::CreatedAt(attributes.updated_at))?, + external_url: attributes.external_url, + title: attributes.title, + pages: attributes.pages, + translated_language: (attributes.translated_language.as_str()) + .try_into() + .map_err(|_| { + ChapterAttributeConversionError::TranslatedLanguage( + attributes.translated_language, + ) + })?, + version: attributes.version, + }) + } } -fn convert_chapter_images(data: ChapterImagesContent) -> Result { - Ok(ChapterImages { - result: (data.result.as_str()) - .try_into() - .map_err(|_| ChapterImageError::Result(data.result))?, - base_url: data.base_url, - chapter: ChapterImageData { - hash: data.chapter.hash, - data: data.chapter.data, - }, - }) +impl TryFrom for ChapterImages { + type Error = ChapterImageError; + fn try_from(data: ChapterImagesContent) -> Result { + Ok(ChapterImages { + result: (data.result.as_str()) + .try_into() + .map_err(|_| ChapterImageError::Result(data.result))?, + base_url: data.base_url, + chapter: ChapterImageData { + hash: data.chapter.hash, + data: data.chapter.data, + }, + }) + } } pub fn deserialize_chapter_images(json: &str) -> Result { @@ -1364,83 +1381,88 @@ pub fn deserialize_chapter_images(json: &str) -> Result return Err(v), Err(e) => { // If you can't parse the error then there is no point in continuing. - eprintln!("ERROR: {:#?}", e); + eprintln!("ERROR: {e:#?}"); std::process::exit(1); } } } }; - Ok(convert_chapter_images(chapter_images).unwrap()) + Ok(chapter_images.try_into().unwrap()) } -fn convert_data_to_manga(m: ContentData) -> Result { - Ok(Manga { - id: Id(m.id), - data_type: (m.type_name.as_str()).try_into().map_err(|_| { - ResponseConversionError::AttributeError(AttributeConversionError::DataType(m.type_name)) - })?, - attributes: convert_attributes(m.attributes) - .map_err(ResponseConversionError::AttributeError)?, - relationships: { - let mut relationships = Vec::with_capacity(m.relationships.len()); - for m in m.relationships { - relationships.push( - ({ - || { - Ok::(Relationship { - id: Id(m.id), - data_type: m - .type_name - .as_str() - .try_into() - .map_err(|_| AttributeConversionError::DataType(m.type_name))?, - attributes: { - if let Some(attributes) = m.attributes { - Some(CoverAttributes { - created_at: DateTime::parse_from_rfc3339( - &attributes.created_at, - ) - .map_err(|_| { - AttributeConversionError::CreatedAtDateTime( - attributes.created_at.clone(), +impl TryFrom for Manga { + type Error = ResponseConversionError; + fn try_from(m: ContentData) -> Result { + Ok(Manga { + id: Id(m.id), + data_type: (m.type_name.as_str()).try_into().map_err(|_| { + ResponseConversionError::AttributeError(AttributeConversionError::DataType( + m.type_name, + )) + })?, + attributes: m + .attributes + .try_into() + .map_err(ResponseConversionError::AttributeError)?, + relationships: { + let mut relationships = Vec::with_capacity(m.relationships.len()); + for m in m.relationships { + relationships.push( + ({ + || { + Ok::(Relationship { + id: Id(m.id), + data_type: m.type_name.as_str().try_into().map_err(|_| { + AttributeConversionError::DataType(m.type_name) + })?, + attributes: { + if let Some(attributes) = m.attributes { + Some(CoverAttributes { + created_at: DateTime::parse_from_rfc3339( + &attributes.created_at, ) - })?, - updated_at: DateTime::parse_from_rfc3339( - &attributes.created_at, - ) - .map_err(|_| { - AttributeConversionError::CreatedAtDateTime( - attributes.created_at.clone(), - ) - })?, - // TODO: Something should probably be done here - description: String::new(), - file_name: Id(attributes.file_name.clone()), - locale: (attributes.locale.as_str()) - .try_into() .map_err(|_| { - AttributeConversionError::Locale( - attributes.locale.clone(), + AttributeConversionError::CreatedAtDateTime( + attributes.created_at.clone(), ) })?, - version: attributes.version, - volume: match &attributes.volume { - Some(v) => v.parse().ok(), - None => None, - }, - }) - } else { - None - } - }, - related: m.related.clone(), - }) - } - })() - .map_err(ResponseConversionError::AttributeError)?, - ); - } - relationships - }, - }) + updated_at: DateTime::parse_from_rfc3339( + &attributes.created_at, + ) + .map_err(|_| { + AttributeConversionError::CreatedAtDateTime( + attributes.created_at.clone(), + ) + })?, + // TODO: Something should probably be done here + description: String::new(), + file_name: Id(attributes.file_name.clone()), + locale: (attributes.locale.as_str()) + .try_into() + .map_err(|_| { + AttributeConversionError::Locale( + attributes.locale.clone(), + ) + })?, + version: attributes.version, + volume: match &attributes.volume { + Some(v) => v.parse().ok(), + None => None, + }, + }) + } else { + None + } + }, + related: m.related.clone(), + }) + } + })() + .map_err(ResponseConversionError::AttributeError)?, + ); + } + relationships + }, + }) + } } diff --git a/src/select.rs b/src/select.rs index 7a24e6f..6e2c552 100644 --- a/src/select.rs +++ b/src/select.rs @@ -64,7 +64,7 @@ fn get_input() -> Option { _ => return None, }), Err(e) => { - eprintln!("ERROR: {:#?}", e); + eprintln!("ERROR: {e:#?}"); exit(); } _ => None, @@ -72,7 +72,7 @@ fn get_input() -> Option { } Ok(false) => None, Err(e) => { - eprintln!("ERROR: {:#?}", e); + eprintln!("ERROR: {e:#?}"); exit(); } } diff --git a/src/util.rs b/src/util.rs index 275c254..6659f5b 100644 --- a/src/util.rs +++ b/src/util.rs @@ -185,7 +185,7 @@ pub fn choose_volumes(input: &str) -> Option { { Ok(v) => Some(VolumeSelection::List(v)), Err(e) => { - eprintln!("Invalid number in list: {:#?}", e); + eprintln!("Invalid number in list: {e:#?}"); None } } @@ -224,7 +224,7 @@ pub fn choose_chapters(input: &str) -> Option { .map(|m| match m.parse() { Ok(v) => v, Err(e) => { - eprintln!("Invalid input: {:#?}", e); + eprintln!("Invalid input: {e:#?}"); invalid = true; 0. } @@ -247,7 +247,7 @@ pub fn choose_chapters(input: &str) -> Option { } pub fn get_input(msg: &str) -> String { - print!("{}", msg); + print!("{msg}"); io::stdout().flush().expect("failed to flush stdout"); let mut input = String::new(); @@ -404,7 +404,7 @@ pub fn args() -> Config { Some(a) => match a.parse() { Ok(v) => v, Err(e) => { - eprintln!("Failed to parse value for result-limit: {:?}, type: u32", e); + eprintln!("Failed to parse value for result-limit: {e:?}, type: u32"); std::process::exit(1); } },