impl TryFrom instead of having conversion functions

This commit is contained in:
2025-06-25 00:35:35 +02:00
parent 2f381d540b
commit b685c3a831
5 changed files with 396 additions and 374 deletions

View File

@@ -5,13 +5,13 @@ edition = "2021"
[dependencies] [dependencies]
chrono = "0.4.38" chrono = "0.4.38"
crossterm = "0.28.1" crossterm = "0.29"
futures = "0.3.30" futures = "0.3.30"
icy_sixel = "0.1.2" icy_sixel = "0.1.2"
image = { version = "0.25.2", default-features = false, features = ["jpeg", "png"] } image = { version = "0.25.2", default-features = false, features = ["jpeg", "png"] }
reqwest = "0.12.5" reqwest = "0.12.5"
reqwest-middleware = "0.3.2" reqwest-middleware = "0.4"
reqwest-retry = "0.6.0" reqwest-retry = "0.7"
serde = { version = "1.0.204", features = ["derive"] } serde = { version = "1.0.204", features = ["derive"] }
serde_json = "1.0.121" serde_json = "1.0.121"
tokio = { version = "1.39.2", default-features = false, features = ["macros", "rt-multi-thread"] } tokio = { version = "1.39.2", default-features = false, features = ["macros", "rt-multi-thread"] }

View File

@@ -149,7 +149,7 @@ async fn main() {
let mut chapters = match get_chapters(client, &manga.id).await { let mut chapters = match get_chapters(client, &manga.id).await {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
eprintln!("ERROR: {:#?}", e); eprintln!("ERROR: {e:#?}");
std::process::exit(1); std::process::exit(1);
} }
}; };
@@ -458,7 +458,7 @@ async fn select_manga_from_search(
let choice = match select::select(&entries) { let choice = match select::select(&entries) {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
eprintln!("ERROR: Failed to select: {:?}", e); eprintln!("ERROR: Failed to select: {e:?}");
std::process::exit(1); std::process::exit(1);
} }
}; };

View File

@@ -625,21 +625,21 @@ enum ChapterImageError {
Result(String), Result(String),
} }
impl TryInto<State> for &str { impl TryFrom<&str> for State {
type Error = (); type Error = ();
fn try_into(self) -> Result<State, ()> { fn try_from(s: &str) -> Result<Self, Self::Error> {
Ok(match self { Ok(match s {
"published" => State::Published, "published" => State::Published,
_ => return Err(()), _ => return Err(()),
}) })
} }
} }
impl TryInto<ContentRating> for &str { impl TryFrom<&str> for ContentRating {
type Error = (); type Error = ();
fn try_into(self) -> Result<ContentRating, ()> { fn try_from(s: &str) -> Result<Self, Self::Error> {
Ok(match self { Ok(match s {
"safe" => ContentRating::Safe, "safe" => ContentRating::Safe,
"suggestive" => ContentRating::Suggestive, "suggestive" => ContentRating::Suggestive,
"erotica" => ContentRating::Erotica, "erotica" => ContentRating::Erotica,
@@ -648,11 +648,11 @@ impl TryInto<ContentRating> for &str {
}) })
} }
} }
impl TryInto<Language> for &str { impl TryFrom<&str> for Language {
type Error = (); type Error = ();
fn try_into(self) -> Result<Language, ()> { fn try_from(s: &str) -> Result<Self, Self::Error> {
Ok(match self { Ok(match s {
"ab" => Language::Abkhazian, "ab" => Language::Abkhazian,
"aa" => Language::Afar, "aa" => Language::Afar,
"af" => Language::Afrikaans, "af" => Language::Afrikaans,
@@ -883,11 +883,11 @@ impl TryInto<Language> for &str {
}) })
} }
} }
impl TryInto<PublicationDemographic> for &str { impl TryFrom<&str> for PublicationDemographic {
type Error = (); type Error = ();
fn try_into(self) -> Result<PublicationDemographic, ()> { fn try_from(s: &str) -> Result<Self, Self::Error> {
Ok(match self { Ok(match s {
"shounen" => PublicationDemographic::Shounen, "shounen" => PublicationDemographic::Shounen,
"josei" => PublicationDemographic::Josei, "josei" => PublicationDemographic::Josei,
"shoujo" => PublicationDemographic::Shoujo, "shoujo" => PublicationDemographic::Shoujo,
@@ -896,11 +896,11 @@ impl TryInto<PublicationDemographic> for &str {
}) })
} }
} }
impl TryInto<DataType> for &str { impl TryFrom<&str> for DataType {
type Error = (); type Error = ();
fn try_into(self) -> Result<DataType, ()> { fn try_from(s: &str) -> Result<Self, Self::Error> {
Ok(match self { Ok(match s {
"manga" => DataType::Manga, "manga" => DataType::Manga,
"chapter" => DataType::Chapter, "chapter" => DataType::Chapter,
"cover_art" => DataType::CoverArt, "cover_art" => DataType::CoverArt,
@@ -915,11 +915,11 @@ impl TryInto<DataType> for &str {
}) })
} }
} }
impl TryInto<Status> for &str { impl TryFrom<&str> for Status {
type Error = (); type Error = ();
fn try_into(self) -> Result<Status, ()> { fn try_from(s: &str) -> Result<Self, Self::Error> {
Ok(match self { Ok(match s {
"ongoing" => Status::Ongoing, "ongoing" => Status::Ongoing,
"completed" => Status::Completed, "completed" => Status::Completed,
"hiatus" => Status::Hiatus, "hiatus" => Status::Hiatus,
@@ -928,21 +928,21 @@ impl TryInto<Status> for &str {
}) })
} }
} }
impl TryInto<ResponseResult> for &str { impl TryFrom<&str> for ResponseResult {
type Error = (); type Error = ();
fn try_into(self) -> Result<ResponseResult, ()> { fn try_from(s: &str) -> Result<Self, Self::Error> {
match self { match s {
"ok" => Ok(ResponseResult::Ok), "ok" => Ok(ResponseResult::Ok),
_ => Err(()), _ => Err(()),
} }
} }
} }
impl TryInto<Response> for &str { impl TryFrom<&str> for Response {
type Error = (); type Error = ();
fn try_into(self) -> Result<Response, ()> { fn try_from(s: &str) -> Result<Self, Self::Error> {
match self { match s {
"collection" => Ok(Response::Collection), "collection" => Ok(Response::Collection),
"entity" => Ok(Response::Entity), "entity" => Ok(Response::Entity),
_ => Err(()), _ => Err(()),
@@ -950,34 +950,35 @@ impl TryInto<Response> for &str {
} }
} }
fn convert_response_to_result( impl TryFrom<SearchResponse> for SearchResult {
search_response: SearchResponse, type Error = ResponseConversionError;
) -> Result<SearchResult, ResponseConversionError> { fn try_from(search_response: SearchResponse) -> Result<Self, Self::Error> {
let response = (search_response.response.as_str()) let response = (search_response.response.as_str())
.try_into() .try_into()
.map_err(|_| ResponseConversionError::Result(search_response.response))?; .map_err(|_| ResponseConversionError::Result(search_response.response))?;
let result: ResponseResult = (search_response.result.as_str()) let result: ResponseResult = (search_response.result.as_str())
.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 mut data: Result<Vec<Manga>, ResponseConversionError> =
Ok(Vec::with_capacity(search_response.data.len())); Ok(Vec::with_capacity(search_response.data.len()));
for m in search_response.data { for m in search_response.data {
if let Ok(ref mut d) = data { if let Ok(ref mut d) = data {
match convert_data_to_manga(m) { match m.try_into() {
Ok(v) => d.push(v), Ok(v) => d.push(v),
Err(e) => { Err(e) => {
data = Err(e); data = Err(e);
break; break;
}
} }
} }
} }
Ok(SearchResult {
response,
result,
data: data?,
})
} }
Ok(SearchResult {
response,
result,
data: data?,
})
} }
#[derive(Debug)] #[derive(Debug)]
@@ -1023,192 +1024,198 @@ impl Display for ContentRating {
} }
} }
fn convert_attributes( impl TryFrom<ContentAttributes> for MangaAttributes {
attributes: ContentAttributes, type Error = AttributeConversionError;
) -> Result<MangaAttributes, AttributeConversionError> { fn try_from(attributes: ContentAttributes) -> Result<Self, Self::Error> {
Ok(MangaAttributes { Ok(MangaAttributes {
title: attributes.title, title: attributes.title,
alt_titles: attributes.alt_titles, alt_titles: attributes.alt_titles,
description: attributes.description, description: attributes.description,
is_locked: attributes.is_locked, is_locked: attributes.is_locked,
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(|_| AttributeConversionError::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),
Err(_) => { Err(_) => {
if s.is_empty() { if s.is_empty() {
None None
} else { } else {
return Err(AttributeConversionError::LastVolume(s)); return Err(AttributeConversionError::LastVolume(s));
}
} }
} },
None => None,
}, },
None => None, last_chapter: match attributes.last_chapter {
}, Some(n) => match n.parse() {
last_chapter: match attributes.last_chapter { Ok(v) => Some(v),
Some(n) => match n.parse() { Err(_) => {
Ok(v) => Some(v), if n.is_empty() {
Err(_) => { None
if n.is_empty() { } else {
None return Err(AttributeConversionError::LastVolume(n));
} else { }
return Err(AttributeConversionError::LastVolume(n));
} }
} },
None => None,
}, },
None => None, publication_demographic: match attributes.publication_demographic {
}, Some(s) => Some(
publication_demographic: match attributes.publication_demographic { (s.as_str())
Some(s) => Some( .try_into()
(s.as_str()) .map_err(|_| AttributeConversionError::PublicationDemographic(s))?,
.try_into() ),
.map_err(|_| AttributeConversionError::PublicationDemographic(s))?, None => None,
), },
None => None, status: (attributes.status.as_str())
}, .try_into()
status: (attributes.status.as_str()) .map_err(|_| AttributeConversionError::Status(attributes.status))?,
.try_into() year: attributes.year,
.map_err(|_| AttributeConversionError::Status(attributes.status))?, content_rating: attributes
year: attributes.year, .content_rating
content_rating: attributes .as_str()
.content_rating .try_into()
.as_str() .map_err(|_| AttributeConversionError::ContentRating(attributes.content_rating))?,
.try_into() tags: {
.map_err(|_| AttributeConversionError::ContentRating(attributes.content_rating))?, let mut tags = Vec::with_capacity(attributes.tags.len());
tags: { for m in attributes.tags {
let mut tags = Vec::with_capacity(attributes.tags.len()); tags.push(({
for m in attributes.tags { || {
tags.push(({ Ok::<Tag, AttributeConversionError>(Tag {
|| { data_type: (m.type_name.as_str())
Ok::<Tag, AttributeConversionError>(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, AttributeConversionError>(
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::<Option<Language>, AttributeConversionError>(match m {
Some(s) => Some(
(s.as_str())
.try_into() .try_into()
.map_err(|_| AttributeConversionError::Language(s))?, .map_err(|_| AttributeConversionError::DataType(m.type_name))?,
), id: Id(m.id),
None => None, relationships: {
}) let mut relationships =
} Vec::with_capacity(m.relationships.len());
})()?); for m in m.relationships {
} relationships.push(({
av || {
}, Ok::<Relationship, AttributeConversionError>(
latest_uploaded_chapter: attributes Relationship {
.latest_uploaded_chapter id: Id(m.id),
.as_ref() data_type: (m.type_name.as_str())
.map(|m| Id(m.clone())), .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::<Option<Language>, 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 { pub fn deserialize_id_query(json: &str) -> IdQueryResult {
let id_query_response: IdQueryResponse = match serde_json::from_str(json) { let id_query_response: IdQueryResponse = match serde_json::from_str(json) {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
eprintln!("ERROR: {:#?}", e); eprintln!("ERROR: {e:#?}");
std::fs::write("out.json", json).unwrap(); std::fs::write("out.json", json).unwrap();
std::process::exit(1); std::process::exit(1);
} }
}; };
convert_id_query(id_query_response).unwrap() id_query_response.try_into().unwrap()
} }
fn convert_id_query(response: IdQueryResponse) -> Result<IdQueryResult, AttributeConversionError> { impl TryFrom<IdQueryResponse> for IdQueryResult {
Ok(IdQueryResult { type Error = AttributeConversionError;
result: response.result.as_str().try_into().unwrap(), fn try_from(response: IdQueryResponse) -> Result<Self, Self::Error> {
response: response.response.as_str().try_into().unwrap(), Ok(IdQueryResult {
data: convert_data_to_manga(response.data).unwrap(), 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 { pub fn deserialize_chapter_feed(json: &str) -> ChapterFeed {
let chapter_feed_response: ChapterFeedResponse = match serde_json::from_str(json) { let chapter_feed_response: ChapterFeedResponse = match serde_json::from_str(json) {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
eprintln!("ERROR: {:#?}", e); eprintln!("ERROR: {e:#?}");
std::fs::write("chapter_feed.json", json).unwrap(); std::fs::write("chapter_feed.json", json).unwrap();
std::process::exit(1); std::process::exit(1);
} }
}; };
convert_chapter_feed(chapter_feed_response).unwrap() chapter_feed_response.try_into().unwrap()
} }
pub fn deserializer(json: &str) -> SearchResult { pub fn deserializer(json: &str) -> SearchResult {
let search_response: SearchResponse = match serde_json::from_str(json) { let search_response: SearchResponse = match serde_json::from_str(json) {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
eprintln!("ERROR: {:#?}", e); eprintln!("ERROR: {e:#?}");
std::fs::write("search_result.json", json).unwrap(); std::fs::write("search_result.json", json).unwrap();
std::process::exit(1); std::process::exit(1);
} }
}; };
let search_result = convert_response_to_result(search_response); let search_result = search_response.try_into();
match search_result { match search_result {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
eprintln!("ERROR: Failed to convert search response: {:#?}", e); eprintln!("ERROR: Failed to convert search response: {e:#?}");
std::process::exit(1); std::process::exit(1);
} }
} }
@@ -1235,66 +1242,70 @@ enum ChapterRelationshipError {
Id(String), Id(String),
} }
fn convert_chapter_feed( impl TryFrom<ChapterFeedResponse> for ChapterFeed {
feed: ChapterFeedResponse, type Error = ChapterFeedConversionError;
) -> Result<ChapterFeed, ChapterFeedConversionError> {
// Now this is a bit of an abomination. It uses closures such that you can use the ? syntax fn try_from(feed: ChapterFeedResponse) -> Result<Self, Self::Error> {
// sugar to return an error. Also uses for loops instead of iterators since they do not take // Now this is a bit of an abomination. It uses closures such that you can use the ? syntax
// ownership. I think I should have just kept the iterators. // sugar to return an error. Also uses for loops instead of iterators since they do not take
let mut data: Vec<Chapter> = Vec::with_capacity(feed.data.len()); // ownership. I think I should have just kept the iterators.
for m in feed.data { let mut data: Vec<Chapter> = Vec::with_capacity(feed.data.len());
let chapter: Chapter = ({ for m in feed.data {
|| { let chapter: Chapter = ({
Ok::<Chapter, ChapterConversionError>(Chapter { || {
data_type: (m.type_name.as_str()) Ok::<Chapter, ChapterConversionError>(Chapter {
.try_into() data_type: (m.type_name.as_str())
.map_err(|_| ChapterConversionError::DataType(m.type_name))?, .try_into()
id: Id(m.id), .map_err(|_| ChapterConversionError::DataType(m.type_name))?,
attributes: convert_chapter_attributes(m.attributes) id: Id(m.id),
.map_err(ChapterConversionError::Attributes)?, attributes: m
relationships: { .attributes
let mut relationships = Vec::with_capacity(m.relationships.len()); .try_into()
for r in m.relationships { .map_err(ChapterConversionError::Attributes)?,
relationships.push( relationships: {
({ let mut relationships = Vec::with_capacity(m.relationships.len());
|| { for r in m.relationships {
Ok({ relationships.push(
ChapterRelationship { ({
data_type: (r.type_name.as_str()) || {
.try_into() Ok({
.map_err(|_| { ChapterRelationship {
ChapterRelationshipError::TypeData( data_type: (r.type_name.as_str())
r.type_name, .try_into()
) .map_err(|_| {
})?, ChapterRelationshipError::TypeData(
id: Id(r.id), r.type_name,
} )
}) })?,
} id: Id(r.id),
})() }
.map_err(ChapterConversionError::Relationship)?, })
); }
} })()
relationships .map_err(ChapterConversionError::Relationship)?,
}, );
}) }
} relationships
})() },
.map_err(ChapterFeedConversionError::Chapter)?; })
data.push(chapter); }
})()
.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)] #[derive(Debug)]
@@ -1307,53 +1318,59 @@ enum ChapterAttributeConversionError {
TranslatedLanguage(String), TranslatedLanguage(String),
} }
fn convert_chapter_attributes( impl TryFrom<ChapterAttributesContent> for ChapterAttributes {
attributes: ChapterAttributesContent, type Error = ChapterAttributeConversionError;
) -> Result<ChapterAttributes, ChapterAttributeConversionError> { fn try_from(attributes: ChapterAttributesContent) -> Result<Self, Self::Error> {
Ok(ChapterAttributes { Ok(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(ChapterAttributeConversionError::Volume(v.to_owned())),
},
None => None,
}, },
None => None, chapter: match &attributes.chapter {
}, Some(v) => match v.parse() {
chapter: match &attributes.chapter { Ok(v) => Some(v),
Some(v) => match v.parse() { Err(_) => return Err(ChapterAttributeConversionError::Chapter(v.to_owned())),
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))?,
created_at: DateTime::parse_from_rfc3339(&attributes.created_at) published_at: DateTime::parse_from_rfc3339(&attributes.publish_at)
.map_err(|_| ChapterAttributeConversionError::CreatedAt(attributes.created_at))?, .map_err(|_| ChapterAttributeConversionError::CreatedAt(attributes.publish_at))?,
published_at: DateTime::parse_from_rfc3339(&attributes.publish_at) updated_at: DateTime::parse_from_rfc3339(&attributes.updated_at)
.map_err(|_| ChapterAttributeConversionError::CreatedAt(attributes.publish_at))?, .map_err(|_| ChapterAttributeConversionError::CreatedAt(attributes.updated_at))?,
updated_at: DateTime::parse_from_rfc3339(&attributes.updated_at) external_url: attributes.external_url,
.map_err(|_| ChapterAttributeConversionError::CreatedAt(attributes.updated_at))?, title: attributes.title,
external_url: attributes.external_url, pages: attributes.pages,
title: attributes.title, translated_language: (attributes.translated_language.as_str())
pages: attributes.pages, .try_into()
translated_language: (attributes.translated_language.as_str()) .map_err(|_| {
.try_into() ChapterAttributeConversionError::TranslatedLanguage(
.map_err(|_| { attributes.translated_language,
ChapterAttributeConversionError::TranslatedLanguage(attributes.translated_language) )
})?, })?,
version: attributes.version, version: attributes.version,
}) })
}
} }
fn convert_chapter_images(data: ChapterImagesContent) -> Result<ChapterImages, ChapterImageError> { impl TryFrom<ChapterImagesContent> for ChapterImages {
Ok(ChapterImages { type Error = ChapterImageError;
result: (data.result.as_str()) fn try_from(data: ChapterImagesContent) -> Result<Self, Self::Error> {
.try_into() Ok(ChapterImages {
.map_err(|_| ChapterImageError::Result(data.result))?, result: (data.result.as_str())
base_url: data.base_url, .try_into()
chapter: ChapterImageData { .map_err(|_| ChapterImageError::Result(data.result))?,
hash: data.chapter.hash, base_url: data.base_url,
data: data.chapter.data, chapter: ChapterImageData {
}, hash: data.chapter.hash,
}) data: data.chapter.data,
},
})
}
} }
pub fn deserialize_chapter_images(json: &str) -> Result<ChapterImages, ChapterImagesContentError> { pub fn deserialize_chapter_images(json: &str) -> Result<ChapterImages, ChapterImagesContentError> {
@@ -1364,83 +1381,88 @@ pub fn deserialize_chapter_images(json: &str) -> Result<ChapterImages, ChapterIm
Ok(v) => return Err(v), Ok(v) => return Err(v),
Err(e) => { Err(e) => {
// If you can't parse the error then there is no point in continuing. // If you can't parse the error then there is no point in continuing.
eprintln!("ERROR: {:#?}", e); eprintln!("ERROR: {e:#?}");
std::process::exit(1); 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<Manga, ResponseConversionError> { impl TryFrom<ContentData> for Manga {
Ok(Manga { type Error = ResponseConversionError;
id: Id(m.id), fn try_from(m: ContentData) -> Result<Self, Self::Error> {
data_type: (m.type_name.as_str()).try_into().map_err(|_| { Ok(Manga {
ResponseConversionError::AttributeError(AttributeConversionError::DataType(m.type_name)) id: Id(m.id),
})?, data_type: (m.type_name.as_str()).try_into().map_err(|_| {
attributes: convert_attributes(m.attributes) ResponseConversionError::AttributeError(AttributeConversionError::DataType(
.map_err(ResponseConversionError::AttributeError)?, m.type_name,
relationships: { ))
let mut relationships = Vec::with_capacity(m.relationships.len()); })?,
for m in m.relationships { attributes: m
relationships.push( .attributes
({ .try_into()
|| { .map_err(ResponseConversionError::AttributeError)?,
Ok::<Relationship, AttributeConversionError>(Relationship { relationships: {
id: Id(m.id), let mut relationships = Vec::with_capacity(m.relationships.len());
data_type: m for m in m.relationships {
.type_name relationships.push(
.as_str() ({
.try_into() || {
.map_err(|_| AttributeConversionError::DataType(m.type_name))?, Ok::<Relationship, AttributeConversionError>(Relationship {
attributes: { id: Id(m.id),
if let Some(attributes) = m.attributes { data_type: m.type_name.as_str().try_into().map_err(|_| {
Some(CoverAttributes { AttributeConversionError::DataType(m.type_name)
created_at: DateTime::parse_from_rfc3339( })?,
&attributes.created_at, attributes: {
) if let Some(attributes) = m.attributes {
.map_err(|_| { Some(CoverAttributes {
AttributeConversionError::CreatedAtDateTime( created_at: DateTime::parse_from_rfc3339(
attributes.created_at.clone(), &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(|_| { .map_err(|_| {
AttributeConversionError::Locale( AttributeConversionError::CreatedAtDateTime(
attributes.locale.clone(), attributes.created_at.clone(),
) )
})?, })?,
version: attributes.version, updated_at: DateTime::parse_from_rfc3339(
volume: match &attributes.volume { &attributes.created_at,
Some(v) => v.parse().ok(), )
None => None, .map_err(|_| {
}, AttributeConversionError::CreatedAtDateTime(
}) attributes.created_at.clone(),
} else { )
None })?,
} // TODO: Something should probably be done here
}, description: String::new(),
related: m.related.clone(), file_name: Id(attributes.file_name.clone()),
}) locale: (attributes.locale.as_str())
} .try_into()
})() .map_err(|_| {
.map_err(ResponseConversionError::AttributeError)?, AttributeConversionError::Locale(
); attributes.locale.clone(),
} )
relationships })?,
}, 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
},
})
}
} }

View File

@@ -64,7 +64,7 @@ fn get_input() -> Option<Action> {
_ => return None, _ => return None,
}), }),
Err(e) => { Err(e) => {
eprintln!("ERROR: {:#?}", e); eprintln!("ERROR: {e:#?}");
exit(); exit();
} }
_ => None, _ => None,
@@ -72,7 +72,7 @@ fn get_input() -> Option<Action> {
} }
Ok(false) => None, Ok(false) => None,
Err(e) => { Err(e) => {
eprintln!("ERROR: {:#?}", e); eprintln!("ERROR: {e:#?}");
exit(); exit();
} }
} }

View File

@@ -185,7 +185,7 @@ pub fn choose_volumes(input: &str) -> Option<VolumeSelection> {
{ {
Ok(v) => Some(VolumeSelection::List(v)), Ok(v) => Some(VolumeSelection::List(v)),
Err(e) => { Err(e) => {
eprintln!("Invalid number in list: {:#?}", e); eprintln!("Invalid number in list: {e:#?}");
None None
} }
} }
@@ -224,7 +224,7 @@ pub fn choose_chapters(input: &str) -> Option<ChapterSelection> {
.map(|m| match m.parse() { .map(|m| match m.parse() {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
eprintln!("Invalid input: {:#?}", e); eprintln!("Invalid input: {e:#?}");
invalid = true; invalid = true;
0. 0.
} }
@@ -247,7 +247,7 @@ pub fn choose_chapters(input: &str) -> Option<ChapterSelection> {
} }
pub fn get_input(msg: &str) -> String { pub fn get_input(msg: &str) -> String {
print!("{}", msg); print!("{msg}");
io::stdout().flush().expect("failed to flush stdout"); io::stdout().flush().expect("failed to flush stdout");
let mut input = String::new(); let mut input = String::new();
@@ -404,7 +404,7 @@ pub fn args() -> Config {
Some(a) => match a.parse() { Some(a) => match a.parse() {
Ok(v) => v, Ok(v) => v,
Err(e) => { 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); std::process::exit(1);
} }
}, },