simplify with .into_iter()

This commit is contained in:
2025-07-22 01:33:12 +02:00
parent 66daaad6a7
commit 4a795819cc

View File

@@ -5,7 +5,7 @@ use chrono::{DateTime, FixedOffset};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub struct Id(pub String); pub struct Id(pub String);
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@@ -287,7 +287,7 @@ pub enum Response {
Entity, Entity,
} }
#[derive(Debug)] #[derive(Debug, PartialEq)]
pub enum DataType { pub enum DataType {
Manga, Manga,
Chapter, Chapter,
@@ -397,7 +397,7 @@ struct ChapterContent {
struct ChapterAttributesContent { struct ChapterAttributesContent {
volume: Option<String>, volume: Option<String>,
chapter: Option<String>, chapter: Option<String>,
title: String, title: Option<String>,
translated_language: String, translated_language: String,
external_url: Option<String>, external_url: Option<String>,
publish_at: String, publish_at: String,
@@ -420,7 +420,7 @@ pub struct Chapter {
pub struct ChapterAttributes { pub struct ChapterAttributes {
pub volume: Option<f32>, pub volume: Option<f32>,
pub chapter: Option<f32>, pub chapter: Option<f32>,
pub title: String, pub title: Option<String>,
pub translated_language: Language, pub translated_language: Language,
pub external_url: Option<String>, pub external_url: Option<String>,
pub published_at: DateTime<FixedOffset>, pub published_at: DateTime<FixedOffset>,
@@ -540,8 +540,8 @@ struct ContentRelationship {
#[derive(Debug)] #[derive(Debug)]
pub struct ChapterRelationship { pub struct ChapterRelationship {
id: Id, pub id: Id,
data_type: DataType, pub data_type: DataType,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -941,19 +941,11 @@ impl TryFrom<SearchResponse> for SearchResult {
.try_into() .try_into()
.map_err(|_| ResponseConversionError::Result(search_response.result))?; .map_err(|_| ResponseConversionError::Result(search_response.result))?;
let mut data: Result<Vec<Manga>, ResponseConversionError> = let data: Result<Vec<Manga>, Self::Error> = search_response
Ok(Vec::with_capacity(search_response.data.len())); .data
for m in search_response.data { .into_iter()
if let Ok(ref mut d) = data { .map(|m| m.try_into())
match m.try_into() { .collect();
Ok(v) => d.push(v),
Err(e) => {
data = Err(e);
break;
}
}
}
}
Ok(SearchResult { Ok(SearchResult {
response, response,
result, result,
@@ -1235,7 +1227,7 @@ impl TryFrom<ContentAttributes> for MangaAttributes {
links: attributes.links, links: attributes.links,
original_language: (attributes.original_language.as_str()) original_language: (attributes.original_language.as_str())
.try_into() .try_into()
.map_err(|_| AttributeConversionError::Language(attributes.original_language))?, .map_err(|_| Self::Error::Language(attributes.original_language))?,
last_volume: match attributes.last_volume { last_volume: match attributes.last_volume {
Some(s) => match s.parse() { Some(s) => match s.parse() {
Ok(v) => Some(v), Ok(v) => Some(v),
@@ -1243,7 +1235,7 @@ impl TryFrom<ContentAttributes> for MangaAttributes {
if s.is_empty() { if s.is_empty() {
None None
} else { } else {
return Err(AttributeConversionError::LastVolume(s)); return Err(Self::Error::LastVolume(s));
} }
} }
}, },
@@ -1256,7 +1248,7 @@ impl TryFrom<ContentAttributes> for MangaAttributes {
if n.is_empty() { if n.is_empty() {
None None
} else { } else {
return Err(AttributeConversionError::LastVolume(n)); return Err(Self::Error::LastVolume(n));
} }
} }
}, },
@@ -1266,56 +1258,43 @@ impl TryFrom<ContentAttributes> for MangaAttributes {
Some(s) => Some( Some(s) => Some(
(s.as_str()) (s.as_str())
.try_into() .try_into()
.map_err(|_| AttributeConversionError::PublicationDemographic(s))?, .map_err(|_| Self::Error::PublicationDemographic(s))?,
), ),
None => None, None => None,
}, },
status: (attributes.status.as_str()) status: (attributes.status.as_str())
.try_into() .try_into()
.map_err(|_| AttributeConversionError::Status(attributes.status))?, .map_err(|_| Self::Error::Status(attributes.status))?,
year: attributes.year, year: attributes.year,
content_rating: attributes content_rating: attributes
.content_rating .content_rating
.as_str() .as_str()
.try_into() .try_into()
.map_err(|_| AttributeConversionError::ContentRating(attributes.content_rating))?, .map_err(|_| Self::Error::ContentRating(attributes.content_rating))?,
tags: { tags: attributes
let mut tags = Vec::with_capacity(attributes.tags.len()); .tags
for m in attributes.tags { .into_iter()
tags.push(({ .map(|m| {
|| { Ok(Tag {
Ok::<Tag, AttributeConversionError>(Tag {
data_type: (m.type_name.as_str()) data_type: (m.type_name.as_str())
.try_into() .try_into()
.map_err(|_| AttributeConversionError::DataType(m.type_name))?, .map_err(|_| Self::Error::DataType(m.type_name))?,
id: Id(m.id), id: Id(m.id),
relationships: { relationships: m
let mut relationships = .relationships
Vec::with_capacity(m.relationships.len()); .into_iter()
for m in m.relationships { .map(|r| {
relationships.push(({ Ok::<Relationship, Self::Error>(Relationship {
|| { id: Id(r.id),
Ok::<Relationship, AttributeConversionError>( data_type: (r.type_name.as_str())
Relationship {
id: Id(m.id),
data_type: (m.type_name.as_str())
.try_into() .try_into()
.map_err(|_| { .map_err(|_| Self::Error::DataType(r.type_name))?,
AttributeConversionError::DataType(
m.type_name,
)
})?,
// TODO: Do this // TODO: Do this
attributes: None, attributes: None,
related: m.related, related: r.related,
}, })
) })
} .collect::<Result<Vec<Relationship>, Self::Error>>()?,
})(
)?);
}
relationships
},
attributes: TagAttributes { attributes: TagAttributes {
name: m.attributes.name, name: m.attributes.name,
group: m.attributes.group, group: m.attributes.group,
@@ -1326,39 +1305,31 @@ impl TryFrom<ContentAttributes> for MangaAttributes {
}, },
}, },
}) })
} })
})()?); .collect::<Result<Vec<Tag>, Self::Error>>()?,
}
tags
},
state: (attributes.state.as_str()) state: (attributes.state.as_str())
.try_into() .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, chapter_numbers_reset_on_new_volume: attributes.chapter_numbers_reset_on_new_volume,
created_at: DateTime::parse_from_rfc3339(&attributes.created_at).map_err(|_| { created_at: DateTime::parse_from_rfc3339(&attributes.created_at)
AttributeConversionError::CreatedAtDateTime(attributes.created_at.clone()) .map_err(|_| Self::Error::CreatedAtDateTime(attributes.created_at.clone()))?,
})?,
updated_at: DateTime::parse_from_rfc3339(&attributes.updated_at) 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, version: attributes.version,
available_translated_languages: { available_translated_languages: attributes
let mut av = Vec::with_capacity(attributes.available_translated_languages.len()); .available_translated_languages
for m in attributes.available_translated_languages { .into_iter()
av.push(({ .map(|m| {
|| { Ok(match m {
Ok::<Option<Language>, AttributeConversionError>(match m {
Some(s) => Some( Some(s) => Some(
(s.as_str()) (s.as_str())
.try_into() .try_into()
.map_err(|_| AttributeConversionError::Language(s))?, .map_err(|_| Self::Error::Language(s))?,
), ),
None => None, None => None,
}) })
} })
})()?); .collect::<Result<Vec<Option<Language>>, Self::Error>>()?,
}
av
},
latest_uploaded_chapter: attributes latest_uploaded_chapter: attributes
.latest_uploaded_chapter .latest_uploaded_chapter
.as_ref() .as_ref()
@@ -1368,6 +1339,7 @@ impl TryFrom<ContentAttributes> for MangaAttributes {
} }
pub fn deserialize_id_query(json: &str) -> Result<IdQueryResult, IdQueryResultError> { pub fn deserialize_id_query(json: &str) -> Result<IdQueryResult, IdQueryResultError> {
std::fs::write("id_query.json", json).unwrap();
let id_query_response: IdQueryResponse = serde_json::from_str(json) let id_query_response: IdQueryResponse = serde_json::from_str(json)
.map_err(|e| IdQueryResultError::Serde(JsonError::Message(e.to_string())))?; .map_err(|e| IdQueryResultError::Serde(JsonError::Message(e.to_string())))?;
id_query_response id_query_response
@@ -1383,16 +1355,13 @@ impl TryFrom<IdQueryResponse> for IdQueryResult {
.result .result
.as_str() .as_str()
.try_into() .try_into()
.map_err(|_| IdQueryResponseError::Result)?, .map_err(|_| Self::Error::Result)?,
response: response response: response
.response .response
.as_str() .as_str()
.try_into() .try_into()
.map_err(|_| IdQueryResponseError::Response)?, .map_err(|_| Self::Error::Response)?,
data: response data: response.data.try_into().map_err(Self::Error::Data)?,
.data
.try_into()
.map_err(IdQueryResponseError::Data)?,
}) })
} }
} }
@@ -1422,14 +1391,18 @@ impl TryFrom<ChapterFeedResponse> for ChapterFeed {
type Error = ChapterFeedConversionError; type Error = ChapterFeedConversionError;
fn try_from(feed: ChapterFeedResponse) -> Result<Self, Self::Error> { fn try_from(feed: ChapterFeedResponse) -> Result<Self, Self::Error> {
// Now this is a bit of an abomination. It uses closures such that you can use the ? syntax Ok(ChapterFeed {
// sugar to return an error. Also uses for loops instead of iterators since they do not take result: (feed.result.as_str())
// ownership. I think I should have just kept the iterators. .try_into()
let mut data: Vec<Chapter> = Vec::with_capacity(feed.data.len()); .map_err(|_| Self::Error::Result(feed.result))?,
for m in feed.data { response: (feed.response.as_str())
let chapter: Chapter = ({ .try_into()
|| { .map_err(|_| Self::Error::Result(feed.response))?,
Ok::<Chapter, ChapterConversionError>(Chapter { data: feed
.data
.into_iter()
.map(|m| {
Ok(Chapter {
data_type: (m.type_name.as_str()) data_type: (m.type_name.as_str())
.try_into() .try_into()
.map_err(|_| ChapterConversionError::DataType(m.type_name))?, .map_err(|_| ChapterConversionError::DataType(m.type_name))?,
@@ -1438,45 +1411,22 @@ impl TryFrom<ChapterFeedResponse> for ChapterFeed {
.attributes .attributes
.try_into() .try_into()
.map_err(ChapterConversionError::Attributes)?, .map_err(ChapterConversionError::Attributes)?,
relationships: { relationships: m
let mut relationships = Vec::with_capacity(m.relationships.len()); .relationships
for r in m.relationships { .into_iter()
relationships.push( .map(|r| {
({ Ok(ChapterRelationship {
|| { data_type: (r.type_name.as_str()).try_into().map_err(|_| {
Ok({ ChapterRelationshipError::TypeData(r.type_name)
ChapterRelationship {
data_type: (r.type_name.as_str())
.try_into()
.map_err(|_| {
ChapterRelationshipError::TypeData(
r.type_name,
)
})?, })?,
id: Id(r.id), id: Id(r.id),
}
}) })
} })
})() .collect::<Result<Vec<ChapterRelationship>, ChapterRelationshipError>>()
.map_err(ChapterConversionError::Relationship)?, .map_err(ChapterConversionError::Relationship)?,
);
}
relationships
},
}) })
} })
})() .collect::<Result<Vec<Chapter>, Self::Error>>()?,
.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, limit: feed.limit,
offset: feed.offset, offset: feed.offset,
total: feed.total, total: feed.total,
@@ -1491,33 +1441,29 @@ impl TryFrom<ChapterAttributesContent> for ChapterAttributes {
volume: match &attributes.volume { volume: match &attributes.volume {
Some(v) => match v.parse() { Some(v) => match v.parse() {
Ok(n) => Some(n), Ok(n) => Some(n),
Err(_) => return Err(ChapterAttributeConversionError::Volume(v.to_owned())), Err(_) => return Err(Self::Error::Volume(v.to_owned())),
}, },
None => None, None => None,
}, },
chapter: match &attributes.chapter { chapter: match &attributes.chapter {
Some(v) => match v.parse() { Some(v) => match v.parse() {
Ok(v) => Some(v), Ok(v) => Some(v),
Err(_) => return Err(ChapterAttributeConversionError::Chapter(v.to_owned())), Err(_) => return Err(Self::Error::Chapter(v.to_owned())),
}, },
None => None, None => None,
}, },
created_at: DateTime::parse_from_rfc3339(&attributes.created_at) 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) 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) 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, external_url: attributes.external_url,
title: attributes.title, title: attributes.title,
pages: attributes.pages, pages: attributes.pages,
translated_language: (attributes.translated_language.as_str()) translated_language: (attributes.translated_language.as_str())
.try_into() .try_into()
.map_err(|_| { .map_err(|_| Self::Error::TranslatedLanguage(attributes.translated_language))?,
ChapterAttributeConversionError::TranslatedLanguage(
attributes.translated_language,
)
})?,
version: attributes.version, version: attributes.version,
}) })
} }
@@ -1529,7 +1475,7 @@ impl TryFrom<ChapterImagesContent> for ChapterImages {
Ok(ChapterImages { Ok(ChapterImages {
result: (data.result.as_str()) result: (data.result.as_str())
.try_into() .try_into()
.map_err(|_| ChapterImageError::Result(data.result))?, .map_err(|_| Self::Error::Result(data.result))?,
base_url: data.base_url, base_url: data.base_url,
chapter: ChapterImageData { chapter: ChapterImageData {
hash: data.chapter.hash, hash: data.chapter.hash,
@@ -1562,23 +1508,20 @@ impl TryFrom<ContentData> for Manga {
Ok(Manga { Ok(Manga {
id: Id(m.id), id: Id(m.id),
data_type: (m.type_name.as_str()).try_into().map_err(|_| { 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: m.attributes.try_into().map_err(Self::Error::Attribute)?,
.attributes relationships: m
.try_into() .relationships
.map_err(ResponseConversionError::Attribute)?, .into_iter()
relationships: { .map(|m| {
let mut relationships = Vec::with_capacity(m.relationships.len()); Ok(Relationship {
for m in m.relationships {
relationships.push(
({
|| {
Ok::<Relationship, AttributeConversionError>(Relationship {
id: Id(m.id), id: Id(m.id),
data_type: m.type_name.as_str().try_into().map_err(|_| { data_type: m
AttributeConversionError::DataType(m.type_name) .type_name
})?, .as_str()
.try_into()
.map_err(|_| AttributeConversionError::DataType(m.type_name))?,
attributes: { attributes: {
if let Some(attributes) = m.attributes { if let Some(attributes) = m.attributes {
Some(CoverAttributes { Some(CoverAttributes {
@@ -1601,13 +1544,13 @@ impl TryFrom<ContentData> for Manga {
// TODO: Something should probably be done here // TODO: Something should probably be done here
description: attributes.description, description: attributes.description,
file_name: Id(attributes.file_name.clone()), file_name: Id(attributes.file_name.clone()),
locale: (attributes.locale.as_str()) locale: (attributes.locale.as_str()).try_into().map_err(
.try_into() |_| {
.map_err(|_| {
AttributeConversionError::Locale( AttributeConversionError::Locale(
attributes.locale.clone(), attributes.locale.clone(),
) )
})?, },
)?,
version: attributes.version, version: attributes.version,
volume: match &attributes.volume { volume: match &attributes.volume {
Some(v) => v.parse().ok(), Some(v) => v.parse().ok(),
@@ -1620,13 +1563,8 @@ impl TryFrom<ContentData> for Manga {
}, },
related: m.related.clone(), related: m.related.clone(),
}) })
} })
})() .collect::<Result<Vec<Relationship>, AttributeConversionError>>()?,
.map_err(ResponseConversionError::Attribute)?,
);
}
relationships
},
}) })
} }
} }
@@ -1638,24 +1576,32 @@ mod tests {
#[test] #[test]
fn id_query() { fn id_query() {
let manga = std::fs::read_to_string("test_data/id_query_hunter_x_hunter.json").unwrap(); 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] #[test]
fn search() { fn search() {
let search_result = std::fs::read_to_string("test_data/search_result.json").unwrap(); 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] #[test]
fn chapter_feed() { 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(); deserialize_chapter_feed(&chapter_feed).unwrap();
} }
#[test] #[test]
fn chapter_images() { 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(); deserialize_chapter_images(&chapter_images).unwrap();
} }
} }