initial
This commit is contained in:
913
src/response_deserializer.rs
Normal file
913
src/response_deserializer.rs
Normal file
@@ -0,0 +1,913 @@
|
||||
// TODO: Remove this
|
||||
#![allow(unused)]
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use serde::Deserialize;
|
||||
use std::fmt::Display;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Id(String);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ResponseResult {
|
||||
Ok,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Language {
|
||||
Turkish,
|
||||
Korean,
|
||||
SpanishLatinAmerican,
|
||||
Hungarian,
|
||||
BrazilianPortugese,
|
||||
English,
|
||||
Japanese,
|
||||
JapaneseRomaji,
|
||||
Italian,
|
||||
Russian,
|
||||
Indonesian,
|
||||
Bulgarian,
|
||||
Hebrew,
|
||||
Spanish,
|
||||
Esperanto,
|
||||
Polish,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Status {
|
||||
Completed,
|
||||
Ongoing,
|
||||
Hiatus,
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ContentRating {
|
||||
Safe,
|
||||
Suggestive,
|
||||
Erotica,
|
||||
Pornographic,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PublicationDemographic {
|
||||
Shounen,
|
||||
Shoujo,
|
||||
Seinen,
|
||||
Josei,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum State {
|
||||
Published,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Response {
|
||||
Collection,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DataType {
|
||||
Manga,
|
||||
Chapter,
|
||||
CoverArt,
|
||||
Author,
|
||||
Artist,
|
||||
ScanlationGroup,
|
||||
Tag,
|
||||
User,
|
||||
CustomList,
|
||||
Creator,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SearchResult {
|
||||
pub result: ResponseResult,
|
||||
pub response: Response,
|
||||
pub data: Vec<Manga>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct ChapterImagesContent {
|
||||
result: String,
|
||||
base_url: String,
|
||||
chapter: ChapterImageDataContent,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct ChapterImageDataContent {
|
||||
hash: String,
|
||||
data: Vec<String>,
|
||||
}
|
||||
|
||||
pub struct ChapterImageData {
|
||||
pub hash: String,
|
||||
pub data: Vec<String>,
|
||||
}
|
||||
|
||||
pub struct ChapterImages {
|
||||
pub result: ResponseResult,
|
||||
pub base_url: String,
|
||||
pub chapter: ChapterImageData,
|
||||
}
|
||||
|
||||
pub struct ChapterFeed {
|
||||
pub result: ResponseResult,
|
||||
pub response: Response,
|
||||
pub data: Vec<Chapter>,
|
||||
pub limit: u32,
|
||||
pub offset: u32,
|
||||
pub total: u32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct ChapterFeedResponse {
|
||||
result: String,
|
||||
response: String,
|
||||
data: Vec<ChapterContent>,
|
||||
limit: u32,
|
||||
offset: u32,
|
||||
total: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MangaAttributes {
|
||||
pub title: Titles,
|
||||
pub alt_titles: Vec<AltTitles>,
|
||||
pub description: Description,
|
||||
pub is_locked: bool,
|
||||
pub links: Option<Links>,
|
||||
pub original_language: Language,
|
||||
pub last_volume: Option<u32>,
|
||||
pub last_chapter: Option<u32>,
|
||||
pub publication_demographic: Option<PublicationDemographic>,
|
||||
pub status: Status,
|
||||
pub year: Option<u32>,
|
||||
pub content_rating: ContentRating,
|
||||
pub tags: Vec<Tag>,
|
||||
pub state: State,
|
||||
pub chapter_numbers_reset_on_new_volume: bool,
|
||||
pub created_at: DateTime<FixedOffset>,
|
||||
pub updated_at: DateTime<FixedOffset>,
|
||||
pub version: u32,
|
||||
pub available_translated_languages: Vec<Option<Language>>,
|
||||
pub latest_uploaded_chapter: Option<Id>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct ChapterContent {
|
||||
id: String,
|
||||
#[serde(rename = "type")]
|
||||
type_name: String,
|
||||
attributes: ChapterAttributesContent,
|
||||
relationships: Vec<ContentRelationShip>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct ChapterAttributesContent {
|
||||
volume: Option<String>,
|
||||
chapter: Option<String>,
|
||||
title: String,
|
||||
translated_language: String,
|
||||
external_url: Option<String>,
|
||||
publish_at: String,
|
||||
readable_at: String,
|
||||
created_at: String,
|
||||
updated_at: String,
|
||||
pages: u32,
|
||||
version: u32,
|
||||
}
|
||||
|
||||
pub struct Chapter {
|
||||
pub id: Id,
|
||||
pub data_type: DataType,
|
||||
pub attributes: ChapterAttributes,
|
||||
pub relationships: Vec<ChapterRelationShip>,
|
||||
}
|
||||
|
||||
pub struct ChapterAttributes {
|
||||
pub volume: Option<u32>,
|
||||
pub chapter: Option<f32>,
|
||||
pub title: String,
|
||||
pub translated_language: Language,
|
||||
pub external_url: Option<String>,
|
||||
pub published_at: DateTime<FixedOffset>,
|
||||
pub created_at: DateTime<FixedOffset>,
|
||||
pub updated_at: DateTime<FixedOffset>,
|
||||
pub pages: u32,
|
||||
pub version: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Manga {
|
||||
pub id: Id,
|
||||
pub data_type: DataType,
|
||||
pub attributes: MangaAttributes,
|
||||
pub relationships: Vec<RelationShip>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct SearchResponse {
|
||||
result: String,
|
||||
response: String,
|
||||
data: Vec<ContentData>,
|
||||
limit: u32,
|
||||
offset: u32,
|
||||
total: u32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct ContentData {
|
||||
id: String,
|
||||
#[serde(rename = "type")]
|
||||
type_name: String,
|
||||
attributes: ContentAttributes,
|
||||
relationships: Vec<ContentRelationShip>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct ContentAttributes {
|
||||
title: Titles,
|
||||
alt_titles: Vec<AltTitles>,
|
||||
description: Description,
|
||||
is_locked: bool,
|
||||
links: Option<Links>,
|
||||
original_language: String,
|
||||
last_volume: Option<String>,
|
||||
last_chapter: Option<String>,
|
||||
publication_demographic: Option<String>,
|
||||
status: String,
|
||||
year: Option<u32>,
|
||||
content_rating: String,
|
||||
tags: Vec<ContentTag>,
|
||||
state: String,
|
||||
chapter_numbers_reset_on_new_volume: bool,
|
||||
created_at: String,
|
||||
updated_at: String,
|
||||
version: u32,
|
||||
available_translated_languages: Vec<Option<String>>,
|
||||
latest_uploaded_chapter: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct ContentCoverAttributes {
|
||||
description: String,
|
||||
volume: Option<String>,
|
||||
file_name: String,
|
||||
locale: String,
|
||||
created_at: String,
|
||||
updated_at: String,
|
||||
version: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CoverAttributes {
|
||||
pub description: String,
|
||||
pub volume: Option<u32>,
|
||||
pub file_name: Id,
|
||||
pub locale: Language,
|
||||
pub created_at: DateTime<FixedOffset>,
|
||||
pub updated_at: DateTime<FixedOffset>,
|
||||
pub version: u32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct ContentTag {
|
||||
id: String,
|
||||
#[serde(rename = "type")]
|
||||
type_name: String,
|
||||
attributes: TagAttributes,
|
||||
relationships: Vec<ContentRelationShip>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Tag {
|
||||
pub id: Id,
|
||||
pub data_type: DataType,
|
||||
pub attributes: TagAttributes,
|
||||
pub relationships: Vec<RelationShip>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
struct ContentRelationShip {
|
||||
id: String,
|
||||
#[serde(rename = "type")]
|
||||
type_name: String,
|
||||
related: Option<String>,
|
||||
attributes: Option<ContentCoverAttributes>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ChapterRelationShip {
|
||||
id: Id,
|
||||
data_type: DataType,
|
||||
}
|
||||
|
||||
#[derive(Debug)] // TODO: Typo: Relationship
|
||||
pub struct RelationShip {
|
||||
pub id: Id,
|
||||
pub data_type: DataType,
|
||||
pub related: Option<String>,
|
||||
pub attributes: Option<CoverAttributes>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct TagAttributes {
|
||||
pub name: TagName,
|
||||
pub description: Description,
|
||||
pub group: String,
|
||||
pub version: u32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct TagName {
|
||||
pub en: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Default, Clone)]
|
||||
pub struct Links {
|
||||
al: Option<String>,
|
||||
ap: Option<String>,
|
||||
bw: Option<String>,
|
||||
kt: Option<String>,
|
||||
mu: Option<String>,
|
||||
amz: Option<String>,
|
||||
cdj: Option<String>,
|
||||
ebj: Option<String>,
|
||||
mal: Option<String>,
|
||||
engtl: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct Description {
|
||||
en: Option<String>,
|
||||
ru: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct AltTitles {
|
||||
en: Option<String>,
|
||||
ja: Option<String>,
|
||||
ru: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct Titles {
|
||||
pub en: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ResponseConversionError {
|
||||
AttributeError(AttributeConversionError),
|
||||
Response(String),
|
||||
Result(String),
|
||||
ContentType(String),
|
||||
}
|
||||
|
||||
impl TryInto<State> for &str {
|
||||
type Error = ();
|
||||
|
||||
fn try_into(self) -> Result<State, ()> {
|
||||
Ok(match self {
|
||||
"published" => State::Published,
|
||||
_ => return Err(()),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl TryInto<ContentRating> for &str {
|
||||
type Error = ();
|
||||
|
||||
fn try_into(self) -> Result<ContentRating, ()> {
|
||||
Ok(match self {
|
||||
"safe" => ContentRating::Safe,
|
||||
"suggestive" => ContentRating::Suggestive,
|
||||
"erotica" => ContentRating::Erotica,
|
||||
"pornographic" => ContentRating::Pornographic,
|
||||
_ => return Err(()),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl TryInto<Language> for &str {
|
||||
type Error = ();
|
||||
|
||||
fn try_into(self) -> Result<Language, ()> {
|
||||
Ok(match self {
|
||||
"ja" => Language::Japanese,
|
||||
"ja-ro" => Language::JapaneseRomaji,
|
||||
"en" => Language::English,
|
||||
"ru" => Language::Russian,
|
||||
"pt-br" => Language::BrazilianPortugese,
|
||||
"tr" => Language::Turkish,
|
||||
"it" => Language::Italian,
|
||||
"es-la" => Language::SpanishLatinAmerican,
|
||||
"hu" => Language::Hungarian,
|
||||
"bg" => Language::Bulgarian,
|
||||
"id" => Language::Indonesian,
|
||||
"he" => Language::Hebrew,
|
||||
"es" => Language::Spanish,
|
||||
"eo" => Language::Esperanto,
|
||||
"pl" => Language::Polish,
|
||||
"ko" => Language::Korean,
|
||||
_ => return Err(()),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl TryInto<PublicationDemographic> for &str {
|
||||
type Error = ();
|
||||
|
||||
fn try_into(self) -> Result<PublicationDemographic, ()> {
|
||||
Ok(match self {
|
||||
"shounen" => PublicationDemographic::Shounen,
|
||||
"josei" => PublicationDemographic::Josei,
|
||||
"shoujo" => PublicationDemographic::Shoujo,
|
||||
"seinen" => PublicationDemographic::Seinen,
|
||||
_ => return Err(()),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl TryInto<DataType> for &str {
|
||||
type Error = ();
|
||||
|
||||
fn try_into(self) -> Result<DataType, ()> {
|
||||
Ok(match self {
|
||||
"manga" => DataType::Manga,
|
||||
"chapter" => DataType::Chapter,
|
||||
"cover_art" => DataType::CoverArt,
|
||||
"author" => DataType::Author,
|
||||
"artist" => DataType::Artist,
|
||||
"scanlation_group" => DataType::ScanlationGroup,
|
||||
"tag" => DataType::Tag,
|
||||
"user" => DataType::User,
|
||||
"custom_list" => DataType::CustomList,
|
||||
"creator" => DataType::Creator,
|
||||
_ => return Err(()),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl TryInto<Status> for &str {
|
||||
type Error = ();
|
||||
|
||||
fn try_into(self) -> Result<Status, ()> {
|
||||
Ok(match self {
|
||||
"ongoing" => Status::Ongoing,
|
||||
"completed" => Status::Completed,
|
||||
"hiatus" => Status::Hiatus,
|
||||
"cancelled" => Status::Cancelled,
|
||||
_ => return Err(()),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl TryInto<ResponseResult> for &str {
|
||||
type Error = ();
|
||||
|
||||
fn try_into(self) -> Result<ResponseResult, ()> {
|
||||
match self {
|
||||
"ok" => Ok(ResponseResult::Ok),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl TryInto<Response> for &str {
|
||||
type Error = ();
|
||||
|
||||
fn try_into(self) -> Result<Response, ()> {
|
||||
match self {
|
||||
"collection" => Ok(Response::Collection),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_response_to_result(
|
||||
search_response: SearchResponse,
|
||||
) -> Result<SearchResult, ResponseConversionError> {
|
||||
let response = (search_response.response.as_str())
|
||||
.try_into()
|
||||
.map_err(|_| ResponseConversionError::Result(search_response.response.clone()))?;
|
||||
let result: ResponseResult = (search_response.result.as_str())
|
||||
.try_into()
|
||||
.map_err(|_| ResponseConversionError::Result({ search_response.result.clone() }))?;
|
||||
|
||||
let data: Vec<Manga> = search_response
|
||||
.data
|
||||
.iter()
|
||||
.map(|m| {
|
||||
Ok(Manga {
|
||||
id: Id(m.id.clone()),
|
||||
data_type: (m.type_name.as_str()).try_into().map_err(|_| {
|
||||
ResponseConversionError::AttributeError(AttributeConversionError::DataType(
|
||||
m.type_name.clone(),
|
||||
))
|
||||
})?,
|
||||
attributes: convert_attributes(&m.attributes)
|
||||
.map_err(ResponseConversionError::AttributeError)?,
|
||||
relationships: m
|
||||
.relationships
|
||||
.iter()
|
||||
.map(|m| {
|
||||
Ok(RelationShip {
|
||||
id: Id(m.id.clone()),
|
||||
data_type: (m.type_name.as_str()).try_into().map_err(|_| {
|
||||
AttributeConversionError::DataType(m.type_name.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: 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(),
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<RelationShip>, AttributeConversionError>>()
|
||||
.map_err(ResponseConversionError::AttributeError)?,
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<Manga>, ResponseConversionError>>()?;
|
||||
Ok(SearchResult {
|
||||
response,
|
||||
result,
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum AttributeConversionError {
|
||||
Language(String),
|
||||
Locale(String),
|
||||
LastVolume(String),
|
||||
LastChapter(String),
|
||||
CreatedAtDateTime(String),
|
||||
UpdatedAtDateTime(String),
|
||||
State(String),
|
||||
ContentRating(String),
|
||||
Status(String),
|
||||
PublicationDemographic(String),
|
||||
DataType(String),
|
||||
}
|
||||
|
||||
impl Display for Id {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Status {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Ongoing => "ongoing".fmt(f),
|
||||
Self::Completed => "completed".fmt(f),
|
||||
Self::Cancelled => "cancelled".fmt(f),
|
||||
Self::Hiatus => "hiatus".fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ContentRating {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Safe => "safe".fmt(f),
|
||||
Self::Suggestive => "suggestive".fmt(f),
|
||||
Self::Erotica => "erotica".fmt(f),
|
||||
Self::Pornographic => "pornographic".fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_attributes(
|
||||
attributes: &ContentAttributes,
|
||||
) -> Result<MangaAttributes, AttributeConversionError> {
|
||||
Ok(MangaAttributes {
|
||||
title: attributes.title.clone(),
|
||||
alt_titles: attributes.alt_titles.clone(),
|
||||
description: attributes.description.clone(),
|
||||
is_locked: attributes.is_locked,
|
||||
links: attributes.links.clone(),
|
||||
original_language: (attributes.original_language.as_str())
|
||||
.try_into()
|
||||
.map_err(|_| {
|
||||
AttributeConversionError::Language(attributes.original_language.clone())
|
||||
})?,
|
||||
last_volume: match attributes.last_volume.clone() {
|
||||
Some(s) => match s.parse() {
|
||||
Ok(v) => Some(v),
|
||||
Err(_) => {
|
||||
if s.is_empty() {
|
||||
None
|
||||
} else {
|
||||
return Err(AttributeConversionError::LastVolume(s));
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
},
|
||||
last_chapter: match attributes.last_chapter.clone() {
|
||||
Some(n) => match n.parse() {
|
||||
Ok(v) => Some(v),
|
||||
Err(_) => {
|
||||
if n.is_empty() {
|
||||
None
|
||||
} else {
|
||||
return Err(AttributeConversionError::LastVolume(n));
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
},
|
||||
publication_demographic: match attributes.publication_demographic.clone() {
|
||||
Some(s) => Some(
|
||||
(s.as_str())
|
||||
.try_into()
|
||||
.map_err(|_| AttributeConversionError::PublicationDemographic(s.clone()))?,
|
||||
),
|
||||
None => None,
|
||||
},
|
||||
status: (attributes.status.as_str())
|
||||
.try_into()
|
||||
.map_err(|_| AttributeConversionError::Status(attributes.status.clone()))?,
|
||||
year: attributes.year,
|
||||
content_rating: attributes.content_rating.as_str().try_into().map_err(|_| {
|
||||
AttributeConversionError::ContentRating(attributes.content_rating.clone())
|
||||
})?,
|
||||
tags: attributes
|
||||
.tags
|
||||
.clone()
|
||||
.iter()
|
||||
.map(|m| {
|
||||
Ok(Tag {
|
||||
data_type: (m.type_name.as_str())
|
||||
.try_into()
|
||||
.map_err(|_| AttributeConversionError::DataType(m.type_name.clone()))?,
|
||||
id: Id(m.id.clone()),
|
||||
relationships: m
|
||||
.relationships
|
||||
.iter()
|
||||
.map(|m| {
|
||||
Ok(RelationShip {
|
||||
id: Id(m.id.clone()),
|
||||
data_type: (m.type_name.as_str()).try_into().map_err(|_| {
|
||||
AttributeConversionError::DataType(m.type_name.clone())
|
||||
})?,
|
||||
// TODO: Do this
|
||||
attributes: None,
|
||||
related: m.related.clone(),
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<RelationShip>, AttributeConversionError>>()?,
|
||||
attributes: TagAttributes {
|
||||
name: m.attributes.name.clone(),
|
||||
group: m.attributes.group.clone(),
|
||||
version: m.attributes.version,
|
||||
description: Description {
|
||||
en: m.attributes.description.en.clone(),
|
||||
ru: m.attributes.description.ru.clone(),
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<Tag>, AttributeConversionError>>()?,
|
||||
state: (attributes.state.as_str())
|
||||
.try_into()
|
||||
.map_err(|_| AttributeConversionError::State(attributes.state.clone()))?,
|
||||
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.clone())
|
||||
})?,
|
||||
version: attributes.version,
|
||||
available_translated_languages: attributes
|
||||
.available_translated_languages
|
||||
.iter()
|
||||
.map(|m| {
|
||||
Ok(match m {
|
||||
Some(s) => Some(
|
||||
(s.as_str())
|
||||
.try_into()
|
||||
.map_err(|_| AttributeConversionError::Language(s.clone()))?,
|
||||
),
|
||||
None => None,
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<Option<Language>>, AttributeConversionError>>()?,
|
||||
latest_uploaded_chapter: attributes
|
||||
.latest_uploaded_chapter
|
||||
.as_ref()
|
||||
.map(|m| Id(m.clone())),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn deserialize_chapter_feed(json: &str) -> ChapterFeed {
|
||||
let chapter_feed_response: ChapterFeedResponse = match serde_json::from_str(json) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
std::fs::write("out.json", json).unwrap();
|
||||
eprintln!("ERROR: {:#?}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
convert_chapter_feed(chapter_feed_response).unwrap()
|
||||
}
|
||||
|
||||
pub fn deserializer(json: &str) -> SearchResult {
|
||||
let search_response: SearchResponse = match serde_json::from_str(json) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
std::fs::write("out.json", json).unwrap();
|
||||
eprintln!("ERROR: {:#?}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
let search_result = convert_response_to_result(search_response);
|
||||
match search_result {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
eprintln!("ERROR: Failed to convert search response: {:#?}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ChapterFeedConversionError {
|
||||
Result(String),
|
||||
Response(String),
|
||||
Chapter(ChapterConversionError),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ChapterConversionError {
|
||||
DataType(String),
|
||||
Id(String),
|
||||
RelationShip(ChapterRelationShipError),
|
||||
Attributes(ChapterAttributeConversionError),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ChapterRelationShipError {
|
||||
TypeData(String),
|
||||
Id(String),
|
||||
}
|
||||
|
||||
fn convert_chapter_feed(
|
||||
feed: ChapterFeedResponse,
|
||||
) -> Result<ChapterFeed, ChapterFeedConversionError> {
|
||||
Ok(ChapterFeed {
|
||||
result: (feed.result.as_str())
|
||||
.try_into()
|
||||
.map_err(|_| ChapterFeedConversionError::Result(feed.result.clone()))?,
|
||||
response: (feed.response.as_str())
|
||||
.try_into()
|
||||
.map_err(|_| ChapterFeedConversionError::Result(feed.response.clone()))?,
|
||||
data: feed
|
||||
.data
|
||||
.iter()
|
||||
.map(|m| {
|
||||
Ok(Chapter {
|
||||
data_type: (m.type_name.as_str())
|
||||
.try_into()
|
||||
.map_err(|_| ChapterConversionError::DataType(m.type_name.clone()))?,
|
||||
id: Id(m.id.clone()),
|
||||
attributes: convert_chapter_attributes(&m.attributes)
|
||||
.map_err(ChapterConversionError::Attributes)?,
|
||||
relationships: m
|
||||
.relationships
|
||||
.iter()
|
||||
.map(|m| {
|
||||
Ok(ChapterRelationShip {
|
||||
data_type: (m.type_name.as_str()).try_into().map_err(|_| {
|
||||
ChapterRelationShipError::TypeData(m.type_name.clone())
|
||||
})?,
|
||||
id: Id(m.id.clone()),
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<ChapterRelationShip>, ChapterRelationShipError>>()
|
||||
.map_err(ChapterConversionError::RelationShip)?,
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<Chapter>, ChapterConversionError>>()
|
||||
.map_err(ChapterFeedConversionError::Chapter)?,
|
||||
limit: feed.limit,
|
||||
offset: feed.offset,
|
||||
total: feed.total,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ChapterAttributeConversionError {
|
||||
Volume(String),
|
||||
Chapter(String),
|
||||
CreatedAt(String),
|
||||
UpdatedAt(String),
|
||||
PublishedAt(String),
|
||||
TranslatedLanguage(String),
|
||||
}
|
||||
|
||||
fn convert_chapter_attributes(
|
||||
attributes: &ChapterAttributesContent,
|
||||
) -> Result<ChapterAttributes, ChapterAttributeConversionError> {
|
||||
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,
|
||||
},
|
||||
chapter: match &attributes.chapter {
|
||||
Some(v) => match v.parse() {
|
||||
Ok(v) => Some(v),
|
||||
Err(_) => return Err(ChapterAttributeConversionError::Chapter(v.to_owned())),
|
||||
},
|
||||
None => None,
|
||||
},
|
||||
created_at: DateTime::parse_from_rfc3339(&attributes.created_at).map_err(|_| {
|
||||
ChapterAttributeConversionError::CreatedAt(attributes.created_at.clone())
|
||||
})?,
|
||||
published_at: DateTime::parse_from_rfc3339(&attributes.publish_at).map_err(|_| {
|
||||
ChapterAttributeConversionError::CreatedAt(attributes.publish_at.clone())
|
||||
})?,
|
||||
updated_at: DateTime::parse_from_rfc3339(&attributes.updated_at).map_err(|_| {
|
||||
ChapterAttributeConversionError::CreatedAt(attributes.updated_at.clone())
|
||||
})?,
|
||||
external_url: attributes.external_url.clone(),
|
||||
title: attributes.title.clone(),
|
||||
pages: attributes.pages,
|
||||
translated_language: (attributes.translated_language.as_str())
|
||||
.try_into()
|
||||
.map_err(|_| {
|
||||
ChapterAttributeConversionError::TranslatedLanguage(
|
||||
attributes.translated_language.clone(),
|
||||
)
|
||||
})?,
|
||||
version: attributes.version,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ChapterImageError {
|
||||
Result(String),
|
||||
}
|
||||
|
||||
fn convert_chapter_images(data: ChapterImagesContent) -> Result<ChapterImages, ChapterImageError> {
|
||||
Ok(ChapterImages {
|
||||
result: (data.result.as_str())
|
||||
.try_into()
|
||||
.map_err(|_| ChapterImageError::Result(data.result.clone()))?,
|
||||
base_url: data.base_url,
|
||||
chapter: ChapterImageData {
|
||||
hash: data.chapter.hash,
|
||||
data: data.chapter.data,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn deserialize_chapter_images(json: &str) -> ChapterImages {
|
||||
let chapter_images: ChapterImagesContent = match serde_json::from_str(json) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
std::fs::write("out.json", json).unwrap();
|
||||
eprintln!("ERROR: {:#?}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
convert_chapter_images(chapter_images).unwrap()
|
||||
}
|
||||
Reference in New Issue
Block a user