mod at_home_server;
mod auth_tokens;
mod author;
mod chapter;
mod check_token_response;
mod check_username_available;
mod cover;
mod custom_list;
mod error;
mod is_following_response;
mod legacy_id_mapping;
mod login_response;
mod manga;
mod manga_aggregate;
mod manga_links;
mod manga_read_markers;
mod manga_reading_status;
mod manga_reading_statuses;
mod manga_relation;
mod ratings;
mod refresh_token_response;
mod report;
mod scanlation_group;
mod statistics;
mod tag;
mod upload_session;
mod upload_session_file;
mod user;
mod user_report;
mod user_settings;
use std::collections::HashMap;
use mangadex_api_types as types;
use serde::Deserialize;
use uuid::Uuid;
use crate::{ApiData, ApiObject, ApiObjectNoRelationships};
pub use at_home_server::AtHomeServer;
pub use auth_tokens::AuthTokens;
pub use author::AuthorAttributes;
pub use chapter::ChapterAttributes;
pub use check_token_response::CheckTokenResponse;
pub use check_username_available::CheckUsernameAvailableResponse;
pub use cover::CoverAttributes;
pub use custom_list::CustomListAttributes;
pub use is_following_response::IsFollowingResponse;
pub use legacy_id_mapping::LegacyMappingIdAttributes;
pub use login_response::LoginResponse;
pub use manga::MangaAttributes;
pub use manga_aggregate::MangaAggregate;
pub use manga_links::MangaLinks;
pub use manga_read_markers::{MangaReadMarkers, UngroupedMangaReadMarkers};
pub use manga_reading_status::MangaReadingStatus;
pub use manga_reading_statuses::MangaReadingStatuses;
pub use manga_relation::MangaRelationAttributes;
pub use ratings::RatingsList;
pub use refresh_token_response::RefreshTokenResponse;
pub use report::ReportReasonAttributes;
pub use scanlation_group::ScanlationGroupAttributes;
pub use statistics::manga::MangaStatisticsObject;
pub use tag::TagAttributes;
pub use types::error::schema::MangaDexErrorResponse;
use types::error::Result;
use types::{Language, MangaRelation, RelationshipType, ResponseType};
pub use upload_session::UploadSessionResponse;
pub use upload_session_file::{UploadSessionFileAttributes, UploadSessionFileData};
pub use user::UserAttributes;
pub use user_report::UserReportAttributes;
pub use user_settings::UserSettingsAttributes;
pub type AtHomeServerResponse = Result<AtHomeServer>;
pub type AuthorObject = ApiObject<AuthorAttributes>;
pub type AuthorData = ApiData<AuthorObject>;
pub type AuthorResponse = Result<AuthorData>;
pub type AuthorListResponse = Result<Results<AuthorObject>>;
pub type ChapterObject = ApiObject<ChapterAttributes>;
pub type ChapterData = ApiData<ChapterObject>;
pub type ChapterResponse = Result<ChapterData>;
pub type ChapterListResponse = Result<Results<ChapterObject>>;
pub type CoverObject = ApiObject<CoverAttributes>;
pub type CoverData = ApiData<CoverObject>;
pub type CoverResponse = Result<CoverData>;
pub type CoverListResponse = Result<Results<CoverObject>>;
pub type CustomListObject = ApiObject<CustomListAttributes>;
pub type CustomListData = ApiData<CustomListObject>;
pub type CustomListResponse = Result<CustomListData>;
pub type CustomListListResponse = Result<Results<CustomListObject>>;
pub type GroupObject = ApiObject<ScanlationGroupAttributes>;
pub type GroupData = ApiData<GroupObject>;
pub type GroupResponse = Result<GroupData>;
pub type GroupListResponse = Result<Results<GroupObject>>;
pub type IdMappingObject = ApiObject<LegacyMappingIdAttributes>;
pub type IdMappingData = ApiData<IdMappingObject>;
pub type IdMappingListResponse = Result<Results<IdMappingObject>>;
pub type MangaObject = ApiObject<MangaAttributes>;
pub type MangaData = ApiData<MangaObject>;
pub type MangaResponse = Result<MangaData>;
pub type MangaListResponse = Result<Results<MangaObject>>;
pub type MangaAggregateResponse = Result<MangaAggregate>;
pub type UngroupedMangaReadMarkersResponse = Result<UngroupedMangaReadMarkers>;
pub type MangaReadMarkersResponse = Result<MangaReadMarkers>;
pub type MangaReadingStatusResponse = Result<MangaReadingStatus>;
pub type MangaReadingStatusesResponse = Result<MangaReadingStatuses>;
pub type MangaRelationObject = ApiObject<MangaRelationAttributes>;
pub type MangaRelationListResponse = Result<Results<MangaRelationObject>>;
pub type MangaStatisticsResponse = Result<MangaStatisticsObject>;
pub type RatingsResponse = Result<RatingsList>;
pub type ReportReasonObject = ApiObjectNoRelationships<ReportReasonAttributes>;
pub type ReportReasonListResponse = Result<Results<ReportReasonObject>>;
pub type TagObject = ApiObject<TagAttributes>;
pub type TagData = ApiData<TagObject>;
pub type TagResponse = Result<TagData>;
pub type TagListResponse = Result<Results<TagObject>>;
pub type UploadSessionFileObject = ApiObject<UploadSessionFileAttributes>;
pub type UploadSessionFileResponse = Result<UploadSessionFileData<UploadSessionFileObject>>;
pub type UserObject = ApiObject<UserAttributes>;
pub type UserData = ApiData<UserObject>;
pub type UserResponse = Result<UserData>;
pub type UserListResponse = Result<Results<UserObject>>;
pub type UserReportsObject = ApiObject<UserReportAttributes>;
pub type UserReportsData = ApiData<UserReportsObject>;
pub type UserReportsListResponse = Result<Results<UserReportsObject>>;
pub type UserSettingsResponse = Result<UserSettingsAttributes>;
#[derive(Debug, Deserialize, Clone)]
#[allow(clippy::large_enum_variant)]
#[serde(untagged)]
pub enum RelatedAttributes {
Manga(MangaAttributes),
Chapter(ChapterAttributes),
CoverArt(CoverAttributes),
Author(AuthorAttributes),
ScanlationGroup(ScanlationGroupAttributes),
Tag(TagAttributes),
User(UserAttributes),
CustomList(CustomListAttributes),
}
#[derive(Debug, Deserialize, Clone)]
pub struct Relationship {
pub id: Uuid,
#[serde(rename = "type")]
pub type_: RelationshipType,
pub related: Option<MangaRelation>,
pub attributes: Option<RelatedAttributes>,
}
#[derive(Debug, Deserialize, Clone)]
pub struct Results<T> {
pub response: ResponseType,
pub data: Vec<T>,
pub limit: u32,
pub offset: u32,
pub total: u32,
}
pub type LocalizedString = HashMap<Language, String>;
pub(crate) mod localizedstring_array_or_map {
use std::collections::HashMap;
use serde::de::{Deserialize, Deserializer, MapAccess, SeqAccess, Visitor};
use super::LocalizedString;
pub fn deserialize<'de, D>(deserializer: D) -> Result<LocalizedString, D::Error>
where
D: Deserializer<'de>,
{
struct V;
impl<'de> Visitor<'de> for V {
type Value = LocalizedString;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("array or object")
}
fn visit_seq<A>(self, mut _seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
Ok(HashMap::new())
}
fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let de = serde::de::value::MapAccessDeserializer::new(map);
let helper = LocalizedString::deserialize(de)?;
Ok(helper)
}
}
deserializer.deserialize_any(V)
}
}
pub(crate) mod volume_aggregate_array_or_map {
use std::collections::BTreeMap;
use serde::de::{Deserializer, MapAccess, SeqAccess, Visitor};
use super::manga_aggregate::VolumeAggregate;
type VolumeAggregateCollection = Vec<VolumeAggregate>;
const PAD_WIDTH: usize = 5;
pub fn deserialize<'de, D>(deserializer: D) -> Result<VolumeAggregateCollection, D::Error>
where
D: Deserializer<'de>,
{
struct V;
impl<'de> Visitor<'de> for V {
type Value = VolumeAggregateCollection;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("array or object")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut volumes = Vec::new();
while let Some(volume) = seq.next_element::<VolumeAggregate>()? {
volumes.push(volume);
}
Ok(volumes)
}
fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let mut sorting_map = BTreeMap::new();
while let Some((volume_number, volume)) =
map.next_entry::<String, VolumeAggregate>()?
{
let volume_number = if volume_number.contains('.') {
match volume_number.parse::<f64>() {
Ok(_) => {
let (i, f) = volume_number.split_once('.').unwrap();
let i = i.parse::<i32>().unwrap();
format!("{i:0PAD_WIDTH$}.{f}")
}
Err(_) => volume_number,
}
} else {
match volume_number.parse::<i32>() {
Ok(n) => format!("{n:0PAD_WIDTH$}"),
Err(_) => volume_number,
}
};
sorting_map.insert(volume_number, volume);
}
Ok(sorting_map.values().cloned().collect())
}
}
deserializer.deserialize_any(V)
}
}
pub(crate) mod chapter_aggregate_array_or_map {
use std::collections::BTreeMap;
use serde::de::{Deserializer, MapAccess, SeqAccess, Visitor};
use super::manga_aggregate::ChapterAggregate;
const PAD_WIDTH: usize = 5;
type ChapterAggregateCollection = Vec<ChapterAggregate>;
pub fn deserialize<'de, D>(deserializer: D) -> Result<ChapterAggregateCollection, D::Error>
where
D: Deserializer<'de>,
{
struct V;
impl<'de> Visitor<'de> for V {
type Value = ChapterAggregateCollection;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("array or object")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut chapters = Vec::new();
while let Some(chapter) = seq.next_element::<ChapterAggregate>()? {
chapters.push(chapter);
}
Ok(chapters)
}
fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let mut sorting_map = BTreeMap::new();
while let Some((chapter_number, chapter)) =
map.next_entry::<String, ChapterAggregate>()?
{
let chapter_number = if chapter_number.contains('.') {
match chapter_number.parse::<f64>() {
Ok(_) => {
let (i, f) = chapter_number.split_once('.').unwrap();
let i = i.parse::<i32>().unwrap();
format!("{i:0PAD_WIDTH$}.{f}")
}
Err(_) => chapter_number,
}
} else {
match chapter_number.parse::<i32>() {
Ok(n) => format!("{n:0PAD_WIDTH$}"),
Err(_) => chapter_number,
}
};
sorting_map.insert(chapter_number, chapter);
}
Ok(sorting_map.values().cloned().collect())
}
}
deserializer.deserialize_any(V)
}
}
pub(crate) mod manga_links_array_or_struct {
use serde::de::{Deserialize, Deserializer, MapAccess, SeqAccess, Visitor};
use crate::v5::MangaLinks;
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<MangaLinks>, D::Error>
where
D: Deserializer<'de>,
{
struct OptionMangaLinksVisitor;
impl<'de> Visitor<'de> for OptionMangaLinksVisitor {
type Value = Option<MangaLinks>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("some or none")
}
fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(None)
}
fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
let manga_links = d.deserialize_any(MangaLinksVisitor)?;
let manga_links = if manga_links.has_no_links() {
None
} else {
Some(manga_links)
};
Ok(manga_links)
}
fn visit_unit<E>(self) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(None)
}
}
struct MangaLinksVisitor;
impl<'de> Visitor<'de> for MangaLinksVisitor {
type Value = MangaLinks;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("array or map")
}
fn visit_seq<A>(self, mut _seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
Ok(Self::Value::default())
}
fn visit_map<M>(self, map: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
Deserialize::deserialize(serde::de::value::MapAccessDeserializer::new(map))
}
}
deserializer.deserialize_option(OptionMangaLinksVisitor)
}
}
pub(crate) mod language_array_or_skip_null {
use mangadex_api_types::Language;
use serde::de::{Deserializer, SeqAccess, Visitor};
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Language>, D::Error>
where
D: Deserializer<'de>,
{
struct V;
impl<'de> Visitor<'de> for V {
type Value = Vec<Language>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a sequence of languages")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut languages = Vec::new();
while let Some(language) = seq.next_element::<Option<Language>>()? {
if let Some(language) = language {
languages.push(language);
}
}
Ok(languages)
}
}
deserializer.deserialize_seq(V)
}
}