Compare commits
2 Commits
352ac3bd1a
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 2f2c0c6bf5 | |||
| 8daa4f0863 |
@@ -4,7 +4,7 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[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"] }
|
crossterm = { version = "0.29", default-features = false, features = ["events"] }
|
||||||
futures = { version = "0.3.30", default-features = false }
|
futures = { version = "0.3.30", default-features = false }
|
||||||
icy_sixel = { version = "0.1.2", default-features = false }
|
icy_sixel = { version = "0.1.2", default-features = false }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
response_deserializer,
|
response_deserializer,
|
||||||
response_deserializer::{Chapter, ChapterFeed, Id, Manga},
|
types::{Chapter, ChapterFeed, Id, Manga},
|
||||||
util::{ConfigSelectionType, CoverSize, Selection, SelectionRange, SelectionType},
|
util::{ConfigSelectionType, CoverSize, Selection, SelectionRange, SelectionType},
|
||||||
BASE,
|
BASE,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -176,6 +176,13 @@ pub enum PublicationDemographicError {
|
|||||||
InvalidValue,
|
InvalidValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum ParseLangErr {
|
||||||
|
#[error("found invalid lang")]
|
||||||
|
Invalid,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Error, Deserialize)]
|
#[derive(Debug, Error, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ApiError {
|
pub struct ApiError {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use crate::error::ParseLangErr;
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub enum Language {
|
pub enum Language {
|
||||||
@@ -62,13 +63,19 @@ impl ExtraLang {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Language {
|
use std::str::FromStr;
|
||||||
pub fn from(s: &str) -> Self {
|
|
||||||
|
impl FromStr for Language {
|
||||||
|
type Err = ParseLangErr;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
if let Some(extra) = ExtraLang::from(s) {
|
if let Some(extra) = ExtraLang::from(s) {
|
||||||
Language::Extra(extra)
|
return Ok(Language::Extra(extra));
|
||||||
} else {
|
|
||||||
Language::Normal(isolang::Language::from_639_1(s).unwrap())
|
|
||||||
}
|
}
|
||||||
|
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
|
// TODO: Only use a single struct for deserializing
|
||||||
use client::{MangaClient, MangaClientBuilder, Search, SearchOptionsBuilder};
|
use client::{MangaClient, MangaClientBuilder, Search, SearchOptionsBuilder};
|
||||||
use language::Language;
|
use language::Language;
|
||||||
use response_deserializer::{Chapter, DataType, Id, Manga};
|
use types::{Chapter, DataType, Id, Manga};
|
||||||
use select::Entry;
|
use select::Entry;
|
||||||
use std::{fs, future::Future, path::Path, pin::Pin, process};
|
use std::{fs, future::Future, path::Path, pin::Pin, process};
|
||||||
use util::{ConfigSearch, CoverSize, SelectionType};
|
use util::{ConfigSearch, CoverSize, SelectionType};
|
||||||
@@ -14,6 +14,7 @@ mod select;
|
|||||||
mod test;
|
mod test;
|
||||||
mod util;
|
mod util;
|
||||||
mod language;
|
mod language;
|
||||||
|
mod types;
|
||||||
|
|
||||||
const BASE: &str = "https://api.mangadex.org";
|
const BASE: &str = "https://api.mangadex.org";
|
||||||
const ONLY_METADATA: bool = true;
|
const ONLY_METADATA: bool = true;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use crate::response_deserializer::{
|
use crate::types::{
|
||||||
Chapter, ContentRating, Manga, PublicationDemographic, State, Status, Titles,
|
Chapter, ContentRating, Manga, PublicationDemographic, State, Status, Titles,
|
||||||
};
|
};
|
||||||
use crate::language::Language;
|
use crate::language::Language;
|
||||||
|
|||||||
@@ -1,311 +1,9 @@
|
|||||||
// TODO: Remove this
|
|
||||||
#![allow(unused)]
|
|
||||||
use crate::error::*;
|
use crate::error::*;
|
||||||
|
use crate::types::*;
|
||||||
use crate::language::Language;
|
use crate::language::Language;
|
||||||
use chrono::{DateTime, FixedOffset};
|
use serde::{Deserialize, Deserializer};
|
||||||
use serde::{Deserialize, Deserializer, Serialize};
|
|
||||||
use std::fmt;
|
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 {
|
impl TryFrom<&str> for ContentRating {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
@@ -447,30 +145,7 @@ pub fn deserialize_chapter_images(json: &str) -> ChapterImages {
|
|||||||
serde_json::from_str(json).unwrap()
|
serde_json::from_str(json).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn opt_string_to_opt_demographic<'de, D>(
|
pub fn opt_string_to_opt_f32<'de, D>(deserializer: D) -> Result<Option<f32>, D::Error>
|
||||||
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
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
@@ -487,34 +162,65 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn string_to_language<'de, D>(deserializer: D) -> Result<Language, D::Error>
|
use serde::de::{self, Visitor};
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
struct LanguageVisitor;
|
||||||
{
|
|
||||||
let s = String::deserialize(deserializer)?;
|
impl<'de> Visitor<'de> for LanguageVisitor {
|
||||||
Ok(Language::from(s.as_str()))
|
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>(
|
impl<'de> Deserialize<'de> for Language {
|
||||||
deserializer: D,
|
fn deserialize<D>(deserializer: D) -> Result<Language, D::Error>
|
||||||
) -> Result<Vec<Option<Language>>, D::Error>
|
where
|
||||||
where
|
D: Deserializer<'de>,
|
||||||
D: Deserializer<'de>,
|
{
|
||||||
{
|
deserializer.deserialize_any(LanguageVisitor)
|
||||||
Ok(Vec::<Option<String>>::deserialize(deserializer)?
|
}
|
||||||
.into_iter()
|
|
||||||
.map(|m| m.map(|v| Language::from(v.as_str())))
|
|
||||||
.collect())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn opt_string_to_opt_id<'de, D>(deserializer: D) -> Result<Option<Id>, D::Error>
|
struct PublicationDemographicVisitor;
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
impl<'de> Visitor<'de> for PublicationDemographicVisitor {
|
||||||
{
|
type Value = PublicationDemographic;
|
||||||
let opt = Option::<String>::deserialize(deserializer)?;
|
|
||||||
match opt {
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
Some(n) => Ok(Some(Id(n))),
|
formatter.write_str("an integer or string for for converting to PublicationDemographic")
|
||||||
None => Ok(None),
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
292
src/types.rs
Normal file
292
src/types.rs
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
#![allow(unused)]
|
||||||
|
use crate::language::Language;
|
||||||
|
use crate::response_deserializer::opt_string_to_opt_f32;
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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>>,
|
||||||
|
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>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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(()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
response_deserializer,
|
response_deserializer,
|
||||||
response_deserializer::ChapterImages,
|
types::ChapterImages,
|
||||||
Chapter, Id, BASE,
|
Chapter, Id, BASE,
|
||||||
};
|
};
|
||||||
use icy_sixel::{DiffusionMethod, MethodForLargest, MethodForRep, PixelFormat, Quality};
|
use icy_sixel::{DiffusionMethod, MethodForLargest, MethodForRep, PixelFormat, Quality};
|
||||||
|
|||||||
Reference in New Issue
Block a user