From 4a795819cc1a6cbefa18f1a992ab11bf848f3f48 Mon Sep 17 00:00:00 2001 From: Vegard Matthey Date: Tue, 22 Jul 2025 01:33:12 +0200 Subject: [PATCH] simplify with .into_iter() --- src/response_deserializer.rs | 398 +++++++++++++++-------------------- 1 file changed, 172 insertions(+), 226 deletions(-) diff --git a/src/response_deserializer.rs b/src/response_deserializer.rs index e885973..c8de26a 100644 --- a/src/response_deserializer.rs +++ b/src/response_deserializer.rs @@ -5,7 +5,7 @@ use chrono::{DateTime, FixedOffset}; use serde::{Deserialize, Serialize}; use std::fmt; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Id(pub String); #[derive(Debug, PartialEq)] @@ -287,7 +287,7 @@ pub enum Response { Entity, } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum DataType { Manga, Chapter, @@ -397,7 +397,7 @@ struct ChapterContent { struct ChapterAttributesContent { volume: Option, chapter: Option, - title: String, + title: Option, translated_language: String, external_url: Option, publish_at: String, @@ -420,7 +420,7 @@ pub struct Chapter { pub struct ChapterAttributes { pub volume: Option, pub chapter: Option, - pub title: String, + pub title: Option, pub translated_language: Language, pub external_url: Option, pub published_at: DateTime, @@ -540,8 +540,8 @@ struct ContentRelationship { #[derive(Debug)] pub struct ChapterRelationship { - id: Id, - data_type: DataType, + pub id: Id, + pub data_type: DataType, } #[derive(Debug)] @@ -941,19 +941,11 @@ impl TryFrom for SearchResult { .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 m.try_into() { - Ok(v) => d.push(v), - Err(e) => { - data = Err(e); - break; - } - } - } - } + let data: Result, Self::Error> = search_response + .data + .into_iter() + .map(|m| m.try_into()) + .collect(); Ok(SearchResult { response, result, @@ -1235,7 +1227,7 @@ impl TryFrom for MangaAttributes { links: attributes.links, original_language: (attributes.original_language.as_str()) .try_into() - .map_err(|_| AttributeConversionError::Language(attributes.original_language))?, + .map_err(|_| Self::Error::Language(attributes.original_language))?, last_volume: match attributes.last_volume { Some(s) => match s.parse() { Ok(v) => Some(v), @@ -1243,7 +1235,7 @@ impl TryFrom for MangaAttributes { if s.is_empty() { None } else { - return Err(AttributeConversionError::LastVolume(s)); + return Err(Self::Error::LastVolume(s)); } } }, @@ -1256,7 +1248,7 @@ impl TryFrom for MangaAttributes { if n.is_empty() { None } else { - return Err(AttributeConversionError::LastVolume(n)); + return Err(Self::Error::LastVolume(n)); } } }, @@ -1266,99 +1258,78 @@ impl TryFrom for MangaAttributes { Some(s) => Some( (s.as_str()) .try_into() - .map_err(|_| AttributeConversionError::PublicationDemographic(s))?, + .map_err(|_| Self::Error::PublicationDemographic(s))?, ), None => None, }, status: (attributes.status.as_str()) .try_into() - .map_err(|_| AttributeConversionError::Status(attributes.status))?, + .map_err(|_| Self::Error::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, - }, - }, + .map_err(|_| Self::Error::ContentRating(attributes.content_rating))?, + tags: attributes + .tags + .into_iter() + .map(|m| { + Ok(Tag { + data_type: (m.type_name.as_str()) + .try_into() + .map_err(|_| Self::Error::DataType(m.type_name))?, + id: Id(m.id), + relationships: m + .relationships + .into_iter() + .map(|r| { + Ok::(Relationship { + id: Id(r.id), + data_type: (r.type_name.as_str()) + .try_into() + .map_err(|_| Self::Error::DataType(r.type_name))?, + // TODO: Do this + attributes: None, + related: r.related, + }) }) - } - })()?); - } - tags - }, + .collect::, Self::Error>>()?, + 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, + }, + }, + }) + }) + .collect::, Self::Error>>()?, state: (attributes.state.as_str()) .try_into() - .map_err(|_| AttributeConversionError::State(attributes.state))?, + .map_err(|_| Self::Error::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()) - })?, + created_at: DateTime::parse_from_rfc3339(&attributes.created_at) + .map_err(|_| Self::Error::CreatedAtDateTime(attributes.created_at.clone()))?, updated_at: DateTime::parse_from_rfc3339(&attributes.updated_at) - .map_err(|_| AttributeConversionError::UpdatedAtDateTime(attributes.created_at))?, + .map_err(|_| Self::Error::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 - }, + available_translated_languages: attributes + .available_translated_languages + .into_iter() + .map(|m| { + Ok(match m { + Some(s) => Some( + (s.as_str()) + .try_into() + .map_err(|_| Self::Error::Language(s))?, + ), + None => None, + }) + }) + .collect::>, Self::Error>>()?, latest_uploaded_chapter: attributes .latest_uploaded_chapter .as_ref() @@ -1368,6 +1339,7 @@ impl TryFrom for MangaAttributes { } pub fn deserialize_id_query(json: &str) -> Result { + std::fs::write("id_query.json", json).unwrap(); let id_query_response: IdQueryResponse = serde_json::from_str(json) .map_err(|e| IdQueryResultError::Serde(JsonError::Message(e.to_string())))?; id_query_response @@ -1383,16 +1355,13 @@ impl TryFrom for IdQueryResult { .result .as_str() .try_into() - .map_err(|_| IdQueryResponseError::Result)?, + .map_err(|_| Self::Error::Result)?, response: response .response .as_str() .try_into() - .map_err(|_| IdQueryResponseError::Response)?, - data: response - .data - .try_into() - .map_err(IdQueryResponseError::Data)?, + .map_err(|_| Self::Error::Response)?, + data: response.data.try_into().map_err(Self::Error::Data)?, }) } } @@ -1422,14 +1391,18 @@ 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 { + Ok(ChapterFeed { + result: (feed.result.as_str()) + .try_into() + .map_err(|_| Self::Error::Result(feed.result))?, + response: (feed.response.as_str()) + .try_into() + .map_err(|_| Self::Error::Result(feed.response))?, + data: feed + .data + .into_iter() + .map(|m| { + Ok(Chapter { data_type: (m.type_name.as_str()) .try_into() .map_err(|_| ChapterConversionError::DataType(m.type_name))?, @@ -1438,45 +1411,22 @@ impl TryFrom for ChapterFeed { .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 - }, + relationships: m + .relationships + .into_iter() + .map(|r| { + Ok(ChapterRelationship { + data_type: (r.type_name.as_str()).try_into().map_err(|_| { + ChapterRelationshipError::TypeData(r.type_name) + })?, + id: Id(r.id), + }) + }) + .collect::, ChapterRelationshipError>>() + .map_err(ChapterConversionError::Relationship)?, }) - } - })() - .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, + }) + .collect::, Self::Error>>()?, limit: feed.limit, offset: feed.offset, total: feed.total, @@ -1491,33 +1441,29 @@ impl TryFrom for ChapterAttributes { volume: match &attributes.volume { Some(v) => match v.parse() { Ok(n) => Some(n), - Err(_) => return Err(ChapterAttributeConversionError::Volume(v.to_owned())), + Err(_) => return Err(Self::Error::Volume(v.to_owned())), }, None => None, }, chapter: match &attributes.chapter { Some(v) => match v.parse() { Ok(v) => Some(v), - Err(_) => return Err(ChapterAttributeConversionError::Chapter(v.to_owned())), + Err(_) => return Err(Self::Error::Chapter(v.to_owned())), }, None => None, }, created_at: DateTime::parse_from_rfc3339(&attributes.created_at) - .map_err(|_| ChapterAttributeConversionError::CreatedAt(attributes.created_at))?, + .map_err(|_| Self::Error::CreatedAt(attributes.created_at))?, published_at: DateTime::parse_from_rfc3339(&attributes.publish_at) - .map_err(|_| ChapterAttributeConversionError::CreatedAt(attributes.publish_at))?, + .map_err(|_| Self::Error::CreatedAt(attributes.publish_at))?, updated_at: DateTime::parse_from_rfc3339(&attributes.updated_at) - .map_err(|_| ChapterAttributeConversionError::CreatedAt(attributes.updated_at))?, + .map_err(|_| Self::Error::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, - ) - })?, + .map_err(|_| Self::Error::TranslatedLanguage(attributes.translated_language))?, version: attributes.version, }) } @@ -1529,7 +1475,7 @@ impl TryFrom for ChapterImages { Ok(ChapterImages { result: (data.result.as_str()) .try_into() - .map_err(|_| ChapterImageError::Result(data.result))?, + .map_err(|_| Self::Error::Result(data.result))?, base_url: data.base_url, chapter: ChapterImageData { hash: data.chapter.hash, @@ -1562,71 +1508,63 @@ impl TryFrom for Manga { Ok(Manga { id: Id(m.id), data_type: (m.type_name.as_str()).try_into().map_err(|_| { - ResponseConversionError::Attribute(AttributeConversionError::DataType(m.type_name)) + Self::Error::Attribute(AttributeConversionError::DataType(m.type_name)) })?, - attributes: m - .attributes - .try_into() - .map_err(ResponseConversionError::Attribute)?, - 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: m.attributes.try_into().map_err(Self::Error::Attribute)?, + relationships: m + .relationships + .into_iter() + .map(|m| { + 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(), + ) })?, - 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(), - ) - })?, - 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: attributes.description, - 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 - } + 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: attributes.description, + 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, }, - related: m.related.clone(), }) + } else { + None } - })() - .map_err(ResponseConversionError::Attribute)?, - ); - } - relationships - }, + }, + related: m.related.clone(), + }) + }) + .collect::, AttributeConversionError>>()?, }) } } @@ -1638,24 +1576,32 @@ mod tests { #[test] fn id_query() { let manga = std::fs::read_to_string("test_data/id_query_hunter_x_hunter.json").unwrap(); - assert_eq!(deserialize_id_query(&manga).unwrap().result , ResponseResult::Ok); + assert_eq!( + deserialize_id_query(&manga).unwrap().result, + ResponseResult::Ok + ); } #[test] fn search() { let search_result = std::fs::read_to_string("test_data/search_result.json").unwrap(); - assert_eq!(deserialize_search(&search_result).unwrap().result , ResponseResult::Ok); + assert_eq!( + deserialize_search(&search_result).unwrap().result, + ResponseResult::Ok + ); } #[test] fn chapter_feed() { - let chapter_feed = std::fs::read_to_string("test_data/chapter_feed_hunter_x_hunter.json").unwrap(); + let chapter_feed = + std::fs::read_to_string("test_data/chapter_feed_hunter_x_hunter.json").unwrap(); deserialize_chapter_feed(&chapter_feed).unwrap(); } #[test] fn chapter_images() { - let chapter_images = std::fs::read_to_string("test_data/chapter_images_hunter_x_hunter.json").unwrap(); + let chapter_images = + std::fs::read_to_string("test_data/chapter_images_hunter_x_hunter.json").unwrap(); deserialize_chapter_images(&chapter_images).unwrap(); } }