use deserializing properly
This commit is contained in:
@@ -4,7 +4,7 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.38", default-features = false }
|
||||
chrono = { version = "0.4.38", default-features = false, features = ["serde"] }
|
||||
crossterm = { version = "0.29", default-features = false, features = ["events"] }
|
||||
futures = { version = "0.3.30", default-features = false }
|
||||
icy_sixel = { version = "0.1.2", default-features = false }
|
||||
|
||||
@@ -176,6 +176,13 @@ pub enum PublicationDemographicError {
|
||||
InvalidValue,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ParseLangErr {
|
||||
#[error("found invalid lang")]
|
||||
Invalid,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Error, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ApiError {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use serde::{Serialize, Serializer};
|
||||
use std::fmt;
|
||||
use crate::error::ParseLangErr;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum Language {
|
||||
@@ -62,13 +63,19 @@ impl ExtraLang {
|
||||
}
|
||||
}
|
||||
|
||||
impl Language {
|
||||
pub fn from(s: &str) -> Self {
|
||||
use std::str::FromStr;
|
||||
|
||||
impl FromStr for Language {
|
||||
type Err = ParseLangErr;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if let Some(extra) = ExtraLang::from(s) {
|
||||
Language::Extra(extra)
|
||||
} else {
|
||||
Language::Normal(isolang::Language::from_639_1(s).unwrap())
|
||||
return Ok(Language::Extra(extra));
|
||||
}
|
||||
if let Some(lang) = isolang::Language::from_639_1(s) {
|
||||
return Ok(Language::Normal(lang));
|
||||
}
|
||||
Err(Self::Err::Invalid)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// TODO: Only use a single struct for deserializing
|
||||
use client::{MangaClient, MangaClientBuilder, Search, SearchOptionsBuilder};
|
||||
use language::Language;
|
||||
use response_deserializer::{Chapter, DataType, Id, Manga};
|
||||
use types::{Chapter, DataType, Id, Manga};
|
||||
use select::Entry;
|
||||
use std::{fs, future::Future, path::Path, pin::Pin, process};
|
||||
use util::{ConfigSearch, CoverSize, SelectionType};
|
||||
@@ -14,6 +14,7 @@ mod select;
|
||||
mod test;
|
||||
mod util;
|
||||
mod language;
|
||||
mod types;
|
||||
|
||||
const BASE: &str = "https://api.mangadex.org";
|
||||
const ONLY_METADATA: bool = true;
|
||||
|
||||
@@ -1,311 +1,10 @@
|
||||
// TODO: Remove this
|
||||
#![allow(unused)]
|
||||
use crate::error::*;
|
||||
use crate::types::*;
|
||||
use crate::language::Language;
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
||||
pub struct Id(pub String);
|
||||
|
||||
#[derive(Deserialize, Debug, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ResponseResult {
|
||||
Ok,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Status {
|
||||
Completed,
|
||||
Ongoing,
|
||||
Hiatus,
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ContentRating {
|
||||
Safe,
|
||||
Suggestive,
|
||||
Erotica,
|
||||
Pornographic,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum PublicationDemographic {
|
||||
Shounen,
|
||||
Shoujo,
|
||||
Seinen,
|
||||
Josei,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum State {
|
||||
Published,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Response {
|
||||
Collection,
|
||||
Entity,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DataType {
|
||||
Manga,
|
||||
Chapter,
|
||||
CoverArt,
|
||||
Author,
|
||||
Artist,
|
||||
ScanlationGroup,
|
||||
Tag,
|
||||
User,
|
||||
CustomList,
|
||||
Creator,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ChapterImages {
|
||||
pub result: ResponseResult,
|
||||
pub base_url: String,
|
||||
pub chapter: ChapterImageData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ChapterImageData {
|
||||
pub hash: String,
|
||||
pub data: Vec<String>,
|
||||
pub data_saver: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
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)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Chapter {
|
||||
pub id: Id,
|
||||
#[serde(rename = "type")]
|
||||
pub data_type: DataType,
|
||||
pub attributes: ChapterAttributes,
|
||||
pub relationships: Vec<Relationship>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ChapterAttributes {
|
||||
#[serde(deserialize_with = "opt_string_to_opt_f32")]
|
||||
pub volume: Option<f32>,
|
||||
#[serde(deserialize_with = "opt_string_to_opt_f32")]
|
||||
pub chapter: Option<f32>,
|
||||
pub title: Option<String>,
|
||||
#[serde(deserialize_with = "string_to_language")]
|
||||
pub translated_language: Language,
|
||||
pub external_url: Option<String>,
|
||||
pub is_unavailable: bool,
|
||||
#[serde(deserialize_with = "string_to_datetime")]
|
||||
#[serde(rename = "publishAt")]
|
||||
pub published_at: DateTime<FixedOffset>,
|
||||
#[serde(deserialize_with = "string_to_datetime")]
|
||||
pub readable_at: DateTime<FixedOffset>,
|
||||
#[serde(deserialize_with = "string_to_datetime")]
|
||||
pub created_at: DateTime<FixedOffset>,
|
||||
#[serde(deserialize_with = "string_to_datetime")]
|
||||
pub updated_at: DateTime<FixedOffset>,
|
||||
pub pages: u32,
|
||||
pub version: u32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct SearchResult {
|
||||
pub result: ResponseResult,
|
||||
pub response: Response,
|
||||
pub data: Vec<Manga>,
|
||||
pub limit: u32,
|
||||
pub offset: u32,
|
||||
pub total: u32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct IdQueryResult {
|
||||
pub result: ResponseResult,
|
||||
pub response: Response,
|
||||
pub data: Manga,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Manga {
|
||||
pub id: Id,
|
||||
#[serde(rename = "type")]
|
||||
pub data_type: DataType,
|
||||
pub attributes: MangaAttributes,
|
||||
pub relationships: Vec<Relationship>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct MangaAttributes {
|
||||
pub title: Titles,
|
||||
pub alt_titles: Vec<AltTitles>,
|
||||
pub description: Description,
|
||||
pub is_locked: bool,
|
||||
pub links: Option<Links>,
|
||||
pub official_links: Option<Vec<String>>,
|
||||
#[serde(deserialize_with = "string_to_language")]
|
||||
pub original_language: Language,
|
||||
#[serde(deserialize_with = "opt_string_to_opt_f32")]
|
||||
pub last_volume: Option<f32>,
|
||||
#[serde(deserialize_with = "opt_string_to_opt_f32")]
|
||||
pub last_chapter: Option<f32>,
|
||||
#[serde(deserialize_with = "opt_string_to_opt_demographic")]
|
||||
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,
|
||||
#[serde(deserialize_with = "string_to_datetime")]
|
||||
pub created_at: DateTime<FixedOffset>,
|
||||
#[serde(deserialize_with = "string_to_datetime")]
|
||||
pub updated_at: DateTime<FixedOffset>,
|
||||
pub version: u32,
|
||||
#[serde(deserialize_with = "vec_opt_string_to_vec_opt_language")]
|
||||
pub available_translated_languages: Vec<Option<Language>>,
|
||||
#[serde(deserialize_with = "opt_string_to_opt_id")]
|
||||
pub latest_uploaded_chapter: Option<Id>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct CoverAttributes {
|
||||
pub description: String,
|
||||
#[serde(deserialize_with = "opt_string_to_opt_f32")]
|
||||
pub volume: Option<f32>,
|
||||
pub file_name: Id,
|
||||
#[serde(deserialize_with = "string_to_language")]
|
||||
pub locale: Language,
|
||||
#[serde(deserialize_with = "string_to_datetime")]
|
||||
pub created_at: DateTime<FixedOffset>,
|
||||
#[serde(deserialize_with = "string_to_datetime")]
|
||||
pub updated_at: DateTime<FixedOffset>,
|
||||
pub version: u32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Tag {
|
||||
pub id: Id,
|
||||
#[serde(rename = "type")]
|
||||
pub data_type: DataType,
|
||||
pub attributes: TagAttributes,
|
||||
pub relationships: Vec<Relationship>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Relationship {
|
||||
pub id: Id,
|
||||
#[serde(rename = "type")]
|
||||
pub data_type: DataType,
|
||||
pub related: Option<String>,
|
||||
pub attributes: Option<CoverAttributes>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct TagAttributes {
|
||||
pub name: TagName,
|
||||
pub description: Description,
|
||||
pub group: String,
|
||||
pub version: u32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct TagName {
|
||||
pub en: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
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>,
|
||||
raw: Option<String>,
|
||||
nu: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
// #[serde(deny_unknown_fields)]
|
||||
// TODO: Fill
|
||||
pub struct Description {
|
||||
pub en: Option<String>,
|
||||
pub ru: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
// #[serde(deny_unknown_fields)]
|
||||
// TODO: Fill
|
||||
pub struct AltTitles {
|
||||
en: Option<String>,
|
||||
ja: Option<String>,
|
||||
ru: Option<String>,
|
||||
de: Option<String>,
|
||||
lt: Option<String>,
|
||||
ar: Option<String>,
|
||||
zh: Option<String>,
|
||||
hi: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Titles {
|
||||
pub en: String,
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for State {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||
Ok(match s {
|
||||
"published" => Self::Published,
|
||||
_ => return Err(()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for ContentRating {
|
||||
type Error = ();
|
||||
|
||||
@@ -447,29 +146,6 @@ pub fn deserialize_chapter_images(json: &str) -> ChapterImages {
|
||||
serde_json::from_str(json).unwrap()
|
||||
}
|
||||
|
||||
fn opt_string_to_opt_demographic<'de, D>(
|
||||
deserializer: D,
|
||||
) -> Result<Option<PublicationDemographic>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let opt = Option::<String>::deserialize(deserializer)?;
|
||||
match opt {
|
||||
Some(n) => Ok(Some(
|
||||
n.as_str().try_into().map_err(serde::de::Error::custom)?,
|
||||
)),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn string_to_datetime<'de, D>(deserializer: D) -> Result<DateTime<FixedOffset>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
DateTime::parse_from_rfc3339(&s).map_err(serde::de::Error::custom)
|
||||
}
|
||||
|
||||
fn opt_string_to_opt_f32<'de, D>(deserializer: D) -> Result<Option<f32>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
@@ -487,34 +163,65 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn string_to_language<'de, D>(deserializer: D) -> Result<Language, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
Ok(Language::from(s.as_str()))
|
||||
use serde::de::{self, Visitor};
|
||||
|
||||
struct LanguageVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for LanguageVisitor {
|
||||
type Value = Language;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("an integer or string for for converting to Language")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
value.parse().map_err(|_| {
|
||||
E::custom(format!(
|
||||
"invalid str for converting to Language, got `{value}`"
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn vec_opt_string_to_vec_opt_language<'de, D>(
|
||||
deserializer: D,
|
||||
) -> Result<Vec<Option<Language>>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ok(Vec::<Option<String>>::deserialize(deserializer)?
|
||||
.into_iter()
|
||||
.map(|m| m.map(|v| Language::from(v.as_str())))
|
||||
.collect())
|
||||
impl<'de> Deserialize<'de> for Language {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Language, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_any(LanguageVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
fn opt_string_to_opt_id<'de, D>(deserializer: D) -> Result<Option<Id>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let opt = Option::<String>::deserialize(deserializer)?;
|
||||
match opt {
|
||||
Some(n) => Ok(Some(Id(n))),
|
||||
None => Ok(None),
|
||||
struct PublicationDemographicVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for PublicationDemographicVisitor {
|
||||
type Value = PublicationDemographic;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("an integer or string for for converting to PublicationDemographic")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
value.try_into().map_err(|_| {
|
||||
E::custom(format!(
|
||||
"invalid str for converting to PublicationDemographic, got `{value}`"
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for PublicationDemographic {
|
||||
fn deserialize<D>(deserializer: D) -> Result<PublicationDemographic, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_any(PublicationDemographicVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
303
src/types.rs
Normal file
303
src/types.rs
Normal file
@@ -0,0 +1,303 @@
|
||||
#![allow(unused)]
|
||||
use crate::language::Language;
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
||||
pub struct Id(pub String);
|
||||
|
||||
#[derive(Deserialize, Debug, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ResponseResult {
|
||||
Ok,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Status {
|
||||
Completed,
|
||||
Ongoing,
|
||||
Hiatus,
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ContentRating {
|
||||
Safe,
|
||||
Suggestive,
|
||||
Erotica,
|
||||
Pornographic,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum PublicationDemographic {
|
||||
Shounen,
|
||||
Shoujo,
|
||||
Seinen,
|
||||
Josei,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum State {
|
||||
Published,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Response {
|
||||
Collection,
|
||||
Entity,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DataType {
|
||||
Manga,
|
||||
Chapter,
|
||||
CoverArt,
|
||||
Author,
|
||||
Artist,
|
||||
ScanlationGroup,
|
||||
Tag,
|
||||
User,
|
||||
CustomList,
|
||||
Creator,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ChapterImages {
|
||||
pub result: ResponseResult,
|
||||
pub base_url: String,
|
||||
pub chapter: ChapterImageData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ChapterImageData {
|
||||
pub hash: String,
|
||||
pub data: Vec<String>,
|
||||
pub data_saver: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
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)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Chapter {
|
||||
pub id: Id,
|
||||
#[serde(rename = "type")]
|
||||
pub data_type: DataType,
|
||||
pub attributes: ChapterAttributes,
|
||||
pub relationships: Vec<Relationship>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ChapterAttributes {
|
||||
#[serde(deserialize_with = "opt_string_to_opt_f32")]
|
||||
pub volume: Option<f32>,
|
||||
#[serde(deserialize_with = "opt_string_to_opt_f32")]
|
||||
pub chapter: Option<f32>,
|
||||
pub title: Option<String>,
|
||||
pub translated_language: Language,
|
||||
pub external_url: Option<String>,
|
||||
pub is_unavailable: bool,
|
||||
#[serde(rename = "publishAt")]
|
||||
pub published_at: DateTime<FixedOffset>,
|
||||
pub readable_at: DateTime<FixedOffset>,
|
||||
pub created_at: DateTime<FixedOffset>,
|
||||
pub updated_at: DateTime<FixedOffset>,
|
||||
pub pages: u32,
|
||||
pub version: u32,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct SearchResult {
|
||||
pub result: ResponseResult,
|
||||
pub response: Response,
|
||||
pub data: Vec<Manga>,
|
||||
pub limit: u32,
|
||||
pub offset: u32,
|
||||
pub total: u32,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct IdQueryResult {
|
||||
pub result: ResponseResult,
|
||||
pub response: Response,
|
||||
pub data: Manga,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Manga {
|
||||
pub id: Id,
|
||||
#[serde(rename = "type")]
|
||||
pub data_type: DataType,
|
||||
pub attributes: MangaAttributes,
|
||||
pub relationships: Vec<Relationship>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct MangaAttributes {
|
||||
pub title: Titles,
|
||||
pub alt_titles: Vec<AltTitles>,
|
||||
pub description: Description,
|
||||
pub is_locked: bool,
|
||||
pub links: Option<Links>,
|
||||
pub official_links: Option<Vec<String>>,
|
||||
pub original_language: Language,
|
||||
#[serde(deserialize_with = "opt_string_to_opt_f32")]
|
||||
pub last_volume: Option<f32>,
|
||||
#[serde(deserialize_with = "opt_string_to_opt_f32")]
|
||||
pub last_chapter: Option<f32>,
|
||||
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>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct CoverAttributes {
|
||||
pub description: String,
|
||||
#[serde(deserialize_with = "opt_string_to_opt_f32")]
|
||||
pub volume: Option<f32>,
|
||||
pub file_name: Id,
|
||||
pub locale: Language,
|
||||
pub created_at: DateTime<FixedOffset>,
|
||||
pub updated_at: DateTime<FixedOffset>,
|
||||
pub version: u32,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Tag {
|
||||
pub id: Id,
|
||||
#[serde(rename = "type")]
|
||||
pub data_type: DataType,
|
||||
pub attributes: TagAttributes,
|
||||
pub relationships: Vec<Relationship>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Relationship {
|
||||
pub id: Id,
|
||||
#[serde(rename = "type")]
|
||||
pub data_type: DataType,
|
||||
pub related: Option<String>,
|
||||
pub attributes: Option<CoverAttributes>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct TagAttributes {
|
||||
pub name: TagName,
|
||||
pub description: Description,
|
||||
pub group: String,
|
||||
pub version: u32,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct TagName {
|
||||
pub en: String,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Deserialize, Debug, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
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>,
|
||||
raw: Option<String>,
|
||||
nu: Option<String>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Deserialize, Debug)]
|
||||
// #[serde(deny_unknown_fields)]
|
||||
// TODO: Fill
|
||||
pub struct Description {
|
||||
pub en: Option<String>,
|
||||
pub ru: Option<String>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Deserialize, Debug)]
|
||||
// #[serde(deny_unknown_fields)]
|
||||
// TODO: Fill
|
||||
pub struct AltTitles {
|
||||
en: Option<String>,
|
||||
ja: Option<String>,
|
||||
ru: Option<String>,
|
||||
de: Option<String>,
|
||||
lt: Option<String>,
|
||||
ar: Option<String>,
|
||||
zh: Option<String>,
|
||||
hi: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Titles {
|
||||
pub en: String,
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for State {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||
Ok(match s {
|
||||
"published" => Self::Published,
|
||||
_ => return Err(()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user