use crate::{Genre, Language};
use chrono::{serde::ts_seconds, DateTime, Utc};
use serde::Deserialize;
use serde_repr::{Deserialize_repr, Serialize_repr};
use std::collections::BTreeMap as Map;
use std::mem;
#[derive(Serialize_repr, Deserialize_repr, Clone, Debug, PartialEq)]
#[repr(u8)]
pub enum Status {
Ongoing = 1,
Completed = 2,
Cancelled = 3,
Hiatus = 4,
#[serde(other)]
Unknown = 0,
}
impl From<u8> for Status {
fn from(v: u8) -> Status {
if v < 5 {
unsafe { mem::transmute(v) }
} else {
Status::Unknown
}
}
}
impl From<Status> for u8 {
fn from(v: Status) -> u8 {
v as u8
}
}
impl Default for Status {
fn default() -> Self {
Status::Unknown
}
}
#[derive(
Debug, Deserialize_repr, Clone, Copy, PartialEq, PartialOrd, Eq, Ord,
)]
#[repr(u8)]
pub enum Over18 {
No = 0,
Yes = 1,
}
impl From<bool> for Over18 {
fn from(b: bool) -> Self {
if b { Over18::Yes } else { Over18::No }
}
}
impl From<Over18> for bool {
fn from(b: Over18) -> Self {
match b {
Over18::No => false,
Over18::Yes => true,
}
}
}
impl Default for Over18 {
fn default() -> Self {
Over18::No
}
}
#[derive(Deserialize, Default, Clone, Debug, PartialEq)]
pub struct Links {
#[serde(rename(deserialize = "bw"))]
pub book_walker: Option<String>,
#[serde(rename(deserialize = "mu"))]
pub manga_updates: Option<String>,
#[serde(rename(deserialize = "nu"))]
pub novel_updates: Option<String>,
#[serde(rename(deserialize = "amz"))]
pub amazon: Option<String>,
#[serde(rename(deserialize = "cdj"))]
pub cd_japan: Option<String>,
#[serde(rename(deserialize = "ebj"))]
pub ebook_japan: Option<String>,
#[serde(rename(deserialize = "mal"))]
pub my_anime_list: Option<String>,
pub raw: Option<String>,
#[serde(rename(deserialize = "engtl"))]
pub english_translation: Option<String>,
}
#[derive(Deserialize, Clone, Debug, PartialEq)]
pub struct Info {
pub title: String,
pub artist: String,
pub author: String,
pub status: Status,
pub genres: Vec<Genre>,
pub description: String,
pub cover_url: String,
pub last_chapter: String,
#[serde(rename(deserialize = "lang_flag"))]
pub lang: Language,
pub hentai: Over18,
pub links: Option<Links>,
}
#[derive(Deserialize, Clone, Debug, PartialEq)]
pub struct Chapter {
pub volume: String,
pub chapter: String,
pub title: String,
#[serde(rename(deserialize = "lang_code"))]
pub lang: Language,
pub group_id: u32,
pub group_id_2: u32,
pub group_id_3: u32,
pub group_name: String,
pub group_name_2: Option<String>,
pub group_name_3: Option<String>,
#[serde(with = "ts_seconds", default = "Utc::now")]
pub timestamp: DateTime<Utc>,
}
#[derive(Deserialize, Clone, Debug, PartialEq)]
pub struct Data {
pub manga: Info,
#[serde(rename(deserialize = "chapter"), default = "Map::new")]
pub chapters: Map<String, Chapter>,
}
#[derive(Deserialize, Clone, Debug, PartialEq)]
#[serde(tag = "status")]
pub enum Manga {
#[serde(rename(deserialize = "Manga ID does not exist."))]
MangaIDDoesNotExist,
#[serde(rename(deserialize = "OK"))]
Ok(Box<Data>),
}
impl Manga {
pub fn is_ok(&self) -> bool {
match self {
Manga::Ok(_) => true,
_ => false,
}
}
pub fn does_not_exist(&self) -> bool {
match self {
Manga::MangaIDDoesNotExist => true,
_ => false,
}
}
pub fn ok(self) -> Option<Data> {
match self {
Manga::Ok(data) => Some(*data),
_ => None,
}
}
}
#[cfg(test)]
pub mod tests {
use super::*;
use serde_json::from_str;
type R = Result<(), Box<serde_json::Error>>;
use crate::test_data::*;
use std::mem::size_of as sizeof;
#[test]
fn manga_is_pointer_size() {
println!("{}, {}", sizeof::<Manga>(), sizeof::<Box<Data>>());
assert_eq!(sizeof::<Manga>(), sizeof::<Box<u8>>());
}
#[test]
fn it_decodes_the_test_manga() -> Result<(), Box<serde_json::Error>> {
let test_test_manga: Manga = from_str(TEST_MANGA_OK)?;
assert!(test_test_manga.is_ok());
eprintln!("{:?}", test_test_manga);
let _ = test_test_manga.ok().unwrap();
Ok(())
}
#[test]
fn it_decodes_a_manga_without_data() -> R {
let test_no_data: Manga = from_str(TEST_MANGA_UNAVAILABLE)?;
assert!(test_no_data.does_not_exist());
eprintln!("{:?}", test_no_data);
Ok(())
}
#[test]
fn it_decodes_a_large_manga() -> R {
let test_01: Manga = from_str(TEST_MANGA_LARGE)?;
assert!(test_01.is_ok());
eprintln!("{:?}", test_01);
let _ = test_01.ok().unwrap();
Ok(())
}
#[test]
fn it_decodes_another_manga() -> R {
let test_99: Manga = from_str(TEST_MANGA_OK_2)?;
assert!(test_99.is_ok());
eprintln!("{:?}", test_99);
let _ = test_99.ok().unwrap();
Ok(())
}
#[test]
fn it_decodes_a_manga_without_chapters_or_links() -> R {
let test_39000: Manga = from_str(TEST_MANGA_NO_META_OR_CH)?;
assert!(test_39000.is_ok());
eprintln!("{:?}", test_39000);
let _ = test_39000.ok().unwrap();
Ok(())
}
#[test]
fn it_does_not_decode_a_non_manga() -> Result<(), Manga> {
let test_non_manga: Result<Manga, serde_json::Error> =
from_str("{\"status\":\"None\"}");
if test_non_manga.is_err() {
let err = test_non_manga.expect_err("Error not found");
assert!(err.is_data());
eprintln!("{:?}", err);
eprintln!("{}", err);
Ok(())
} else {
Err(test_non_manga.unwrap())
}
}
}