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 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<String>,
chapter: Option<String>,
title: String,
title: Option<String>,
translated_language: String,
external_url: Option<String>,
publish_at: String,
@@ -420,7 +420,7 @@ pub struct Chapter {
pub struct ChapterAttributes {
pub volume: Option<f32>,
pub chapter: Option<f32>,
pub title: String,
pub title: Option<String>,
pub translated_language: Language,
pub external_url: Option<String>,
pub published_at: DateTime<FixedOffset>,
@@ -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<SearchResponse> for SearchResult {
.try_into()
.map_err(|_| ResponseConversionError::Result(search_response.result))?;
let mut data: Result<Vec<Manga>, 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<Vec<Manga>, Self::Error> = search_response
.data
.into_iter()
.map(|m| m.try_into())
.collect();
Ok(SearchResult {
response,
result,
@@ -1235,7 +1227,7 @@ impl TryFrom<ContentAttributes> 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<ContentAttributes> 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<ContentAttributes> for MangaAttributes {
if n.is_empty() {
None
} else {
return Err(AttributeConversionError::LastVolume(n));
return Err(Self::Error::LastVolume(n));
}
}
},
@@ -1266,56 +1258,43 @@ impl TryFrom<ContentAttributes> 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, AttributeConversionError>(Tag {
.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(|_| AttributeConversionError::DataType(m.type_name))?,
.map_err(|_| Self::Error::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, AttributeConversionError>(
Relationship {
id: Id(m.id),
data_type: (m.type_name.as_str())
relationships: m
.relationships
.into_iter()
.map(|r| {
Ok::<Relationship, Self::Error>(Relationship {
id: Id(r.id),
data_type: (r.type_name.as_str())
.try_into()
.map_err(|_| {
AttributeConversionError::DataType(
m.type_name,
)
})?,
.map_err(|_| Self::Error::DataType(r.type_name))?,
// TODO: Do this
attributes: None,
related: m.related,
},
)
}
})(
)?);
}
relationships
},
related: r.related,
})
})
.collect::<Result<Vec<Relationship>, Self::Error>>()?,
attributes: TagAttributes {
name: m.attributes.name,
group: m.attributes.group,
@@ -1326,39 +1305,31 @@ impl TryFrom<ContentAttributes> for MangaAttributes {
},
},
})
}
})()?);
}
tags
},
})
.collect::<Result<Vec<Tag>, 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::<Option<Language>, AttributeConversionError>(match m {
available_translated_languages: attributes
.available_translated_languages
.into_iter()
.map(|m| {
Ok(match m {
Some(s) => Some(
(s.as_str())
.try_into()
.map_err(|_| AttributeConversionError::Language(s))?,
.map_err(|_| Self::Error::Language(s))?,
),
None => None,
})
}
})()?);
}
av
},
})
.collect::<Result<Vec<Option<Language>>, Self::Error>>()?,
latest_uploaded_chapter: attributes
.latest_uploaded_chapter
.as_ref()
@@ -1368,6 +1339,7 @@ impl TryFrom<ContentAttributes> for MangaAttributes {
}
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)
.map_err(|e| IdQueryResultError::Serde(JsonError::Message(e.to_string())))?;
id_query_response
@@ -1383,16 +1355,13 @@ impl TryFrom<IdQueryResponse> 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<ChapterFeedResponse> for ChapterFeed {
type Error = ChapterFeedConversionError;
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
// 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<Chapter> = Vec::with_capacity(feed.data.len());
for m in feed.data {
let chapter: Chapter = ({
|| {
Ok::<Chapter, ChapterConversionError>(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<ChapterFeedResponse> 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,
)
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::<Result<Vec<ChapterRelationship>, ChapterRelationshipError>>()
.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,
})
.collect::<Result<Vec<Chapter>, Self::Error>>()?,
limit: feed.limit,
offset: feed.offset,
total: feed.total,
@@ -1491,33 +1441,29 @@ impl TryFrom<ChapterAttributesContent> 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<ChapterImagesContent> 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,23 +1508,20 @@ impl TryFrom<ContentData> 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, AttributeConversionError>(Relationship {
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)
})?,
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 {
@@ -1601,13 +1544,13 @@ impl TryFrom<ContentData> for Manga {
// 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(|_| {
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(),
@@ -1620,13 +1563,8 @@ impl TryFrom<ContentData> for Manga {
},
related: m.related.clone(),
})
}
})()
.map_err(ResponseConversionError::Attribute)?,
);
}
relationships
},
})
.collect::<Result<Vec<Relationship>, 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();
}
}