1463 lines
43 KiB
Rust
1463 lines
43 KiB
Rust
// TODO: Remove this
|
|
#![allow(unused)]
|
|
use chrono::{DateTime, FixedOffset};
|
|
use serde::Deserialize;
|
|
use std::fmt::Display;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Id(pub String);
|
|
|
|
#[derive(Debug)]
|
|
pub enum ResponseResult {
|
|
Ok,
|
|
}
|
|
|
|
// https://api.mangadex.org/docs/3-enumerations/
|
|
// https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes
|
|
// https://www.loc.gov/standards/iso639-2/php/English_list.php
|
|
// This is a fucking mess, I hate this. I could have just used a library, but I would have to adapt
|
|
// it to the special cases which mangadex require.
|
|
// The two-letter codes are not unique, and the code esentially just choose the first one in the
|
|
// list. Why have identifiers if they are not unique? Who knows what they were smoking when making
|
|
// this. Also Why is there Bouth SouthNdebele and Ndebele, South? Who knows? Why is there Bokmål
|
|
// and Norwegian Bokmål, many questions to ask, but few answers to get. Best part of this is that
|
|
// the updated ISO pdf is behind a paywall. https://www.iso.org/standard/74575.html
|
|
// If the code works 98% of the time and is 95% correct I guess that is "good enough".
|
|
#[derive(Debug)]
|
|
pub enum Language {
|
|
Abkhazian,
|
|
Afar,
|
|
Afrikaans,
|
|
Akan,
|
|
Albanian,
|
|
Amharic,
|
|
Arabic,
|
|
Aragonese,
|
|
Armenian,
|
|
Assamese,
|
|
Avaric,
|
|
Avestan,
|
|
Aymara,
|
|
Azerbaijani,
|
|
Bambara,
|
|
Bashkir,
|
|
Basque,
|
|
Belarusian,
|
|
Bengali,
|
|
Bislama,
|
|
Bosnian,
|
|
BrazilianPortugese,
|
|
Breton,
|
|
Bulgarian,
|
|
Burmese,
|
|
Castilian,
|
|
CastilianSpanish,
|
|
Catalan,
|
|
Central,
|
|
Chamorro,
|
|
Chechen,
|
|
Chewa,
|
|
Chichewa,
|
|
SimplifiedChinese,
|
|
TraditionalChinese,
|
|
Chuang,
|
|
ChurchSlavic,
|
|
ChurchSlavonic,
|
|
Chuvash,
|
|
Cornish,
|
|
Corsican,
|
|
Cree,
|
|
Croatian,
|
|
Czech,
|
|
Danish,
|
|
Dhivehi,
|
|
Divehi,
|
|
Dutch,
|
|
Dzongkha,
|
|
English,
|
|
Esperanto,
|
|
Estonian,
|
|
Ewe,
|
|
Faroese,
|
|
Fijian,
|
|
Finnish,
|
|
Flemish,
|
|
French,
|
|
Fulah,
|
|
Gaelic,
|
|
Galician,
|
|
Ganda,
|
|
Georgian,
|
|
German,
|
|
Gikuyu,
|
|
Greek,
|
|
Greenlandic,
|
|
Guarani,
|
|
Gujarati,
|
|
Gwic,
|
|
Haitian,
|
|
Hausa,
|
|
Hebrew,
|
|
Herero,
|
|
Hindi,
|
|
Hiri,
|
|
Hungarian,
|
|
Icelandic,
|
|
Ido,
|
|
Igbo,
|
|
Indonesian,
|
|
Interlingua,
|
|
Interlingue,
|
|
Inuktitut,
|
|
Inupiaq,
|
|
Irish,
|
|
Italian,
|
|
Japanese,
|
|
Javanese,
|
|
Kalaallisut,
|
|
Kannada,
|
|
Kanuri,
|
|
Kashmiri,
|
|
Kazakh,
|
|
Kikuyu,
|
|
Kinyarwanda,
|
|
Kirghiz,
|
|
Komi,
|
|
Kongo,
|
|
Korean,
|
|
Kuanyama,
|
|
Kurdish,
|
|
Kwanyama,
|
|
Kyrgyz,
|
|
Lao,
|
|
Latin,
|
|
LatinAmericanSpanish,
|
|
Latvian,
|
|
Letzeburgesch,
|
|
Limburgan,
|
|
Limburger,
|
|
Limburgish,
|
|
Lingala,
|
|
Lithuanian,
|
|
LubaKatanga,
|
|
Luxembourgish,
|
|
Macedonian,
|
|
Malagasy,
|
|
Malay,
|
|
Malayalam,
|
|
Maldivian,
|
|
Maltese,
|
|
Manx,
|
|
Maori,
|
|
Marathi,
|
|
Marshallese,
|
|
MiMoldavian,
|
|
Moldovan,
|
|
Mongolian,
|
|
NNauru,
|
|
Navaho,
|
|
Navajo,
|
|
NdebeleNorth,
|
|
NdebeleSouth,
|
|
Ndonga,
|
|
Nepali,
|
|
North,
|
|
Northern,
|
|
Norwegian,
|
|
NorwegianBokmål,
|
|
NorwegianNynorsk,
|
|
Nuosu,
|
|
Nyanja,
|
|
Nynors,
|
|
Occidental,
|
|
Occitan,
|
|
Ojibwa,
|
|
OldBulgarian,
|
|
OldChurchSlavonic,
|
|
OldSlavonic,
|
|
Oriya,
|
|
Oromo,
|
|
Ossetian,
|
|
Ossetic,
|
|
Pali,
|
|
Panjabi,
|
|
Pashto,
|
|
Persian,
|
|
Polish,
|
|
Portuguese,
|
|
ProvençPunjabi,
|
|
Pushto,
|
|
Quechua,
|
|
Romanian,
|
|
RomanizedJapanese,
|
|
RomanizedKorean,
|
|
RomanizedChinese,
|
|
Romansh,
|
|
Rundi,
|
|
Russian,
|
|
Samoan,
|
|
Sango,
|
|
Sanskrit,
|
|
Sardinian,
|
|
Scottish,
|
|
Serbian,
|
|
Shona,
|
|
Sichuan,
|
|
Sindhi,
|
|
Sinhala,
|
|
Sinhalese,
|
|
Slovak,
|
|
Slovenian,
|
|
Somali,
|
|
Sotho,
|
|
SouthNdebele,
|
|
Spanish,
|
|
Sundanese,
|
|
Swahili,
|
|
Swati,
|
|
Swedish,
|
|
Tagalog,
|
|
Tahitian,
|
|
Tajik,
|
|
Tamil,
|
|
Tatar,
|
|
Telugu,
|
|
Thai,
|
|
Tibetan,
|
|
Tigrinya,
|
|
Tonga,
|
|
Tsonga,
|
|
Tswana,
|
|
Turkish,
|
|
Turkmen,
|
|
Twi,
|
|
Uighur,
|
|
Ukrainian,
|
|
Urdu,
|
|
Uyghur,
|
|
Uzbek,
|
|
Valencian,
|
|
Venda,
|
|
Vietnamese,
|
|
Volapük,
|
|
Walloon,
|
|
Welsh,
|
|
Western,
|
|
Wolof,
|
|
Xhosa,
|
|
Yiddish,
|
|
Yoruba,
|
|
Zhuang,
|
|
Zulu,
|
|
}
|
|
|
|
#[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,
|
|
Entity,
|
|
}
|
|
|
|
#[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)]
|
|
pub struct IdQueryResult {
|
|
pub result: ResponseResult,
|
|
pub response: Response,
|
|
pub data: Manga,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
struct ChapterImagesContent {
|
|
result: String,
|
|
base_url: String,
|
|
chapter: ChapterImageDataContent,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ApiError {
|
|
pub id: String,
|
|
pub status: u32,
|
|
pub title: String,
|
|
pub detail: String,
|
|
pub context: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ChapterImagesContentError {
|
|
pub result: String,
|
|
pub errors: Vec<ApiError>,
|
|
}
|
|
|
|
#[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>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
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 IdQueryResponse {
|
|
result: String,
|
|
response: String,
|
|
data: ContentData,
|
|
}
|
|
|
|
#[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),
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum ChapterImageError {
|
|
Result(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 {
|
|
"ab" => Language::Abkhazian,
|
|
"aa" => Language::Afar,
|
|
"af" => Language::Afrikaans,
|
|
"ak" => Language::Akan,
|
|
"sq" => Language::Albanian,
|
|
"am" => Language::Amharic,
|
|
"ar" => Language::Arabic,
|
|
"an" => Language::Aragonese,
|
|
"hy" => Language::Armenian,
|
|
"as" => Language::Assamese,
|
|
"av" => Language::Avaric,
|
|
"ae" => Language::Avestan,
|
|
"ay" => Language::Aymara,
|
|
"az" => Language::Azerbaijani,
|
|
"bm" => Language::Bambara,
|
|
"ba" => Language::Bashkir,
|
|
"eu" => Language::Basque,
|
|
"be" => Language::Belarusian,
|
|
"bn" => Language::Bengali,
|
|
"bi" => Language::Bislama,
|
|
"nb" => Language::NorwegianBokmål,
|
|
"bs" => Language::Bosnian,
|
|
"br" => Language::Breton,
|
|
"bg" => Language::Bulgarian,
|
|
"my" => Language::Burmese,
|
|
"es" => Language::Castilian,
|
|
"ca" => Language::Catalan,
|
|
"km" => Language::Central,
|
|
"ch" => Language::Chamorro,
|
|
"ce" => Language::Chechen,
|
|
"ny" => Language::Chewa,
|
|
"ny" => Language::Chichewa,
|
|
"zh" => Language::SimplifiedChinese,
|
|
"za" => Language::Chuang,
|
|
"cu" => Language::ChurchSlavic,
|
|
"cu" => Language::ChurchSlavonic,
|
|
"cv" => Language::Chuvash,
|
|
"kw" => Language::Cornish,
|
|
"co" => Language::Corsican,
|
|
"cr" => Language::Cree,
|
|
"hr" => Language::Croatian,
|
|
"cs" => Language::Czech,
|
|
"da" => Language::Danish,
|
|
"dv" => Language::Dhivehi,
|
|
"dv" => Language::Divehi,
|
|
"nl" => Language::Dutch,
|
|
"dz" => Language::Dzongkha,
|
|
"en" => Language::English,
|
|
"eo" => Language::Esperanto,
|
|
"et" => Language::Estonian,
|
|
"ee" => Language::Ewe,
|
|
"fo" => Language::Faroese,
|
|
"fj" => Language::Fijian,
|
|
"fi" => Language::Finnish,
|
|
"nl" => Language::Flemish,
|
|
"fr" => Language::French,
|
|
"ff" => Language::Fulah,
|
|
"gd" => Language::Gaelic,
|
|
"gl" => Language::Galician,
|
|
"lg" => Language::Ganda,
|
|
"ka" => Language::Georgian,
|
|
"de" => Language::German,
|
|
"ki" => Language::Gikuyu,
|
|
"el" => Language::Greek,
|
|
"kl" => Language::Greenlandic,
|
|
"gn" => Language::Guarani,
|
|
"gu" => Language::Gujarati,
|
|
"ht" => Language::Haitian,
|
|
"ha" => Language::Hausa,
|
|
"he" => Language::Hebrew,
|
|
"hz" => Language::Herero,
|
|
"hi" => Language::Hindi,
|
|
"ho" => Language::Hiri,
|
|
"hu" => Language::Hungarian,
|
|
"is" => Language::Icelandic,
|
|
"io" => Language::Ido,
|
|
"ig" => Language::Igbo,
|
|
"id" => Language::Indonesian,
|
|
"ia" => Language::Interlingua,
|
|
"ie" => Language::Interlingue,
|
|
"iu" => Language::Inuktitut,
|
|
"ik" => Language::Inupiaq,
|
|
"ga" => Language::Irish,
|
|
"it" => Language::Italian,
|
|
"ja" => Language::Japanese,
|
|
"jv" => Language::Javanese,
|
|
"kl" => Language::Kalaallisut,
|
|
"kn" => Language::Kannada,
|
|
"kr" => Language::Kanuri,
|
|
"ks" => Language::Kashmiri,
|
|
"kk" => Language::Kazakh,
|
|
"ki" => Language::Kikuyu,
|
|
"rw" => Language::Kinyarwanda,
|
|
"ky" => Language::Kirghiz,
|
|
"kv" => Language::Komi,
|
|
"kg" => Language::Kongo,
|
|
"ko" => Language::Korean,
|
|
"kj" => Language::Kuanyama,
|
|
"ku" => Language::Kurdish,
|
|
"kj" => Language::Kwanyama,
|
|
"ky" => Language::Kyrgyz,
|
|
"lo" => Language::Lao,
|
|
"la" => Language::Latin,
|
|
"lv" => Language::Latvian,
|
|
"lb" => Language::Letzeburgesch,
|
|
"li" => Language::Limburgan,
|
|
"li" => Language::Limburger,
|
|
"li" => Language::Limburgish,
|
|
"ln" => Language::Lingala,
|
|
"lt" => Language::Lithuanian,
|
|
"lu" => Language::LubaKatanga,
|
|
"lb" => Language::Luxembourgish,
|
|
"mk" => Language::Macedonian,
|
|
"mg" => Language::Malagasy,
|
|
"ms" => Language::Malay,
|
|
"ml" => Language::Malayalam,
|
|
"dv" => Language::Maldivian,
|
|
"mt" => Language::Maltese,
|
|
"gv" => Language::Manx,
|
|
"mi" => Language::Maori,
|
|
"mr" => Language::Marathi,
|
|
"mh" => Language::Marshallese,
|
|
"ro" => Language::MiMoldavian,
|
|
"ro" => Language::Moldovan,
|
|
"mn" => Language::Mongolian,
|
|
"na" => Language::NNauru,
|
|
"nv" => Language::Navaho,
|
|
"nv" => Language::Navajo,
|
|
"nd" => Language::NdebeleNorth,
|
|
"nr" => Language::NdebeleSouth,
|
|
"ng" => Language::Ndonga,
|
|
"ne" => Language::Nepali,
|
|
"nd" => Language::North,
|
|
"se" => Language::Northern,
|
|
"no" => Language::Norwegian,
|
|
"nb" => Language::NorwegianBokmål,
|
|
"nn" => Language::NorwegianNynorsk,
|
|
"ii" => Language::Nuosu,
|
|
"ny" => Language::Nyanja,
|
|
"nn" => Language::NorwegianNynorsk,
|
|
"ie" => Language::Occidental,
|
|
"oc" => Language::Occitan,
|
|
"oj" => Language::Ojibwa,
|
|
"cu" => Language::OldBulgarian,
|
|
"cu" => Language::OldChurchSlavonic,
|
|
"cu" => Language::OldSlavonic,
|
|
"or" => Language::Oriya,
|
|
"om" => Language::Oromo,
|
|
"os" => Language::Ossetian,
|
|
"os" => Language::Ossetic,
|
|
"pi" => Language::Pali,
|
|
"pa" => Language::Panjabi,
|
|
"ps" => Language::Pashto,
|
|
"fa" => Language::Persian,
|
|
"pl" => Language::Polish,
|
|
"pt" => Language::Portuguese,
|
|
"pa" => Language::ProvençPunjabi,
|
|
"ps" => Language::Pushto,
|
|
"qu" => Language::Quechua,
|
|
"ro" => Language::Romanian,
|
|
"rm" => Language::Romansh,
|
|
"rn" => Language::Rundi,
|
|
"ru" => Language::Russian,
|
|
"sm" => Language::Samoan,
|
|
"sg" => Language::Sango,
|
|
"sa" => Language::Sanskrit,
|
|
"sc" => Language::Sardinian,
|
|
"gd" => Language::Scottish,
|
|
"sr" => Language::Serbian,
|
|
"sn" => Language::Shona,
|
|
"ii" => Language::Sichuan,
|
|
"sd" => Language::Sindhi,
|
|
"si" => Language::Sinhala,
|
|
"si" => Language::Sinhalese,
|
|
"sk" => Language::Slovak,
|
|
"sl" => Language::Slovenian,
|
|
"so" => Language::Somali,
|
|
"st" => Language::Sotho,
|
|
"es" => Language::Spanish,
|
|
"su" => Language::Sundanese,
|
|
"sw" => Language::Swahili,
|
|
"ss" => Language::Swati,
|
|
"sv" => Language::Swedish,
|
|
"tl" => Language::Tagalog,
|
|
"ty" => Language::Tahitian,
|
|
"tg" => Language::Tajik,
|
|
"ta" => Language::Tamil,
|
|
"tt" => Language::Tatar,
|
|
"te" => Language::Telugu,
|
|
"th" => Language::Thai,
|
|
"bo" => Language::Tibetan,
|
|
"ti" => Language::Tigrinya,
|
|
"to" => Language::Tonga,
|
|
"ts" => Language::Tsonga,
|
|
"tn" => Language::Tswana,
|
|
"tr" => Language::Turkish,
|
|
"tk" => Language::Turkmen,
|
|
"tw" => Language::Twi,
|
|
"ug" => Language::Uighur,
|
|
"uk" => Language::Ukrainian,
|
|
"ur" => Language::Urdu,
|
|
"ug" => Language::Uyghur,
|
|
"uz" => Language::Uzbek,
|
|
"ca" => Language::Valencian,
|
|
"ve" => Language::Venda,
|
|
"vi" => Language::Vietnamese,
|
|
"vo" => Language::Volapük,
|
|
"wa" => Language::Walloon,
|
|
"cy" => Language::Welsh,
|
|
"fy" => Language::Western,
|
|
"wo" => Language::Wolof,
|
|
"xh" => Language::Xhosa,
|
|
"yi" => Language::Yiddish,
|
|
"yo" => Language::Yoruba,
|
|
"za" => Language::Zhuang,
|
|
"zu" => Language::Zulu,
|
|
|
|
"zh-ro" => Language::RomanizedChinese,
|
|
"zh" => Language::SimplifiedChinese,
|
|
"zh-hk" => Language::TraditionalChinese,
|
|
"pt" => Language::Portuguese,
|
|
"es" => Language::CastilianSpanish,
|
|
"es-la" => Language::LatinAmericanSpanish,
|
|
"ja-ro" => Language::RomanizedJapanese,
|
|
"pt-br" => Language::BrazilianPortugese,
|
|
|
|
_ => 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),
|
|
"entity" => Ok(Response::Entity),
|
|
_ => 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_id_query(json: &str) -> IdQueryResult {
|
|
let id_query_response: IdQueryResponse = 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_some_test(id_query_response).unwrap()
|
|
}
|
|
|
|
fn convert_some_test(input: IdQueryResponse) -> Result<IdQueryResult, AttributeConversionError> {
|
|
Ok(IdQueryResult {
|
|
result: input.result.as_str().try_into().unwrap(),
|
|
response: input.response.as_str().try_into().unwrap(),
|
|
data: convert_data_to_manga(input.data).unwrap(),
|
|
})
|
|
}
|
|
|
|
// *****
|
|
|
|
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,
|
|
})
|
|
}
|
|
|
|
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) -> Result<ChapterImages, ChapterImagesContentError> {
|
|
let chapter_images: ChapterImagesContent = match serde_json::from_str(json) {
|
|
Ok(v) => v,
|
|
Err(e) => {
|
|
match serde_json::from_str::<ChapterImagesContentError>(json) {
|
|
Ok(v) => return Err(v),
|
|
Err(e) => {
|
|
// If you can't parse the error then there is no point in continuing.
|
|
eprintln!("ERROR: {:#?}", e);
|
|
std::process::exit(1);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
Ok(convert_chapter_images(chapter_images).unwrap())
|
|
}
|
|
|
|
fn convert_data_to_manga(m: ContentData) -> Result<Manga, ResponseConversionError> {
|
|
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)?,
|
|
})
|
|
}
|