use chrono::{DateTime, Utc};
use derive_builder::Builder;
use serde::Serialize;
use uuid::Uuid;
use crate::schema::{manga::*, LanguageCode, NoData};
use crate::{Client, Result};
#[derive(Debug, Serialize, Clone, Builder, Default)]
#[serde(rename_all = "camelCase")]
#[builder(setter(into, strip_option), default)]
pub struct ListManga<'a> {
pub limit: Option<i32>,
pub offset: Option<i32>,
pub title: Option<String>,
#[builder(setter(each = "add_author"))]
pub authors: Vec<&'a Uuid>,
#[builder(setter(each = "add_artist"))]
pub artists: Vec<&'a Uuid>,
pub year: Option<i32>,
#[builder(setter(each = "include_tag"))]
pub included_tags: Vec<&'a Uuid>,
pub included_tags_mode: Option<TagMode>,
#[builder(setter(each = "exclude_tag"))]
pub excluded_tags: Vec<&'a Uuid>,
pub excluded_tags_mode: Option<TagMode>,
#[builder(setter(each = "add_status"))]
pub status: Vec<MangaStatus>,
pub original_language: Vec<String>,
#[builder(setter(each = "add_demographic"))]
pub publication_demographic: Vec<Demographic>,
#[builder(setter(each = "add_manga"))]
#[serde(rename = "ids")]
pub manga_ids: Vec<Uuid>,
pub content_rating: Option<ContentRating>,
pub created_at_since: Option<DateTime<Utc>>,
pub updated_at_since: Option<DateTime<Utc>>,
pub order: Option<MangaOrder>,
}
impl_endpoint! {
GET "/manga",
#[query] ListManga<'_>,
MangaList
}
#[derive(Debug, Serialize, Clone)]
pub struct CreateManga<'a> {
#[serde(flatten)]
pub request: &'a MangaRequest,
}
impl_endpoint! {
POST "/manga",
#[body auth] CreateManga<'_>,
#[flatten_result] MangaResponse
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct GetMangaAggregate<'a> {
#[serde(skip)]
pub manga_id: &'a Uuid,
pub translated_language: Option<LanguageCode>,
}
impl_endpoint! {
GET ("/manga/{:x}/aggregate", manga_id),
#[query] GetMangaAggregate<'_>,
#[flatten_result] MangaAggregateResponse
}
#[derive(Debug, Clone)]
pub struct GetManga<'a> {
pub id: &'a Uuid,
}
impl_endpoint! {
GET ("/manga/{:x}", id),
#[no_data] GetManga<'_>,
#[flatten_result] MangaResponse
}
#[derive(Debug, Serialize, Clone)]
pub struct UpdateManga<'a> {
#[serde(skip)]
pub id: &'a Uuid,
#[serde(flatten)]
pub request: &'a MangaRequest,
}
impl_endpoint! {
PUT ("/manga/{:x}", id),
#[body auth] UpdateManga<'_>,
#[flatten_result] MangaResponse
}
#[derive(Debug, Clone)]
pub struct DeleteManga<'a> {
pub id: &'a Uuid,
}
impl_endpoint! {
DELETE ("/manga/{:x}", id),
#[no_data auth] DeleteManga<'_>,
#[discard_result] Result<NoData>
}
pub struct UnfollowManga<'a> {
pub id: &'a Uuid,
}
impl_endpoint! {
DELETE ("/manga/{:x}/follow", id),
#[no_data auth] UnfollowManga<'_>,
#[discard_result] Result<NoData>
}
#[derive(Debug, Clone)]
pub struct FollowManga<'a> {
pub id: &'a Uuid,
}
impl_endpoint! {
POST ("/manga/{:x}/follow", id),
#[no_data auth] FollowManga<'_>,
#[discard_result] Result<NoData>
}
#[derive(Debug, Clone)]
pub struct GetMangaReadMarkers<'a> {
pub manga_id: &'a Uuid,
}
impl_endpoint! {
GET ("/manga/{:x}/read", manga_id),
#[no_data auth] GetMangaReadMarkers<'_>,
#[flatten_result] MangaReadMarkerResponse
}
#[derive(Serialize)]
pub struct GetBatchMangaReadMarkers<'a> {
pub ids: Vec<&'a Uuid>,
}
impl_endpoint! {
GET "/manga/read",
#[query auth] GetBatchMangaReadMarkers<'_>,
#[flatten_result] MangaReadMarkerResponse
}
pub struct RandomManga;
impl_endpoint! {
GET "/manga/random",
#[no_data] RandomManga,
#[flatten_result] MangaResponse
}
pub struct ListTags;
impl_endpoint! {
GET "/manga/tag",
#[no_data] ListTags,
TagList
}
#[derive(Serialize)]
pub struct AllMangaStatus {
pub status: Option<MangaReadingStatus>,
}
impl_endpoint! {
GET "/manga/status",
#[query auth] AllMangaStatus,
#[flatten_result] Result<MangaReadingStatuses>
}
#[derive(Debug, Clone)]
pub struct GetMangaStatus<'a> {
pub id: &'a Uuid,
}
impl_endpoint! {
GET ("/manga/{:x}/status", id),
#[no_data auth] GetMangaStatus<'_>,
#[no_send] Result<MangaReadingStatusBody>
}
impl GetMangaStatus<'_> {
pub async fn send(&self, client: &Client) -> Result<MangaReadingStatus> {
client.send_request(self).await?.map(|r| r.status)
}
}
#[derive(Debug, Serialize, Clone)]
pub struct UpdateMangaStatus<'a> {
#[serde(skip)]
pub id: &'a Uuid,
pub status: MangaReadingStatus,
}
impl_endpoint! {
POST ("/manga/{:x}/status", id),
#[body auth] UpdateMangaStatus<'_>,
#[discard_result] Result<NoData>
}
#[cfg(test)]
mod tests {
use crate::{
schema::{LanguageCode, ResourceType},
Client,
};
use super::*;
use chrono::prelude::*;
use pretty_assertions::assert_eq;
#[tokio::test]
async fn list_manga() {
let client = Client::default();
let manga = ListManga::default().send(&client).await.unwrap();
assert_eq!(manga.offset, 0);
assert_eq!(manga.limit, 10);
}
#[tokio::test]
async fn view_manga() {
let id = Uuid::parse_str("32d76d19-8a05-4db0-9fc2-e0b0648fe9d0").unwrap();
let client = Client::default();
let manga_result = GetManga { id: &id }.send(&client).await.unwrap();
let manga = manga_result.data;
assert_eq!(manga.id, id);
assert_eq!(
manga
.attributes
.title
.get(&LanguageCode::English)
.map(String::as_str),
Some("Solo Leveling")
);
assert_eq!(manga.attributes.original_language.as_str(), "ko");
assert_eq!(
manga.attributes.created_at,
Utc.ymd(2019, 8, 25).and_hms(10, 51, 55)
);
}
#[tokio::test]
async fn random_manga() {
let client = Client::default();
let manga_result = RandomManga.send(&client).await.unwrap();
let manga = manga_result.data;
assert_eq!(manga.r#type, ResourceType::Manga);
}
#[tokio::test]
async fn tag_list() {
let client = Client::default();
let tag_results = ListTags.send(&client).await.unwrap();
for result in &tag_results {
let tag = &result.as_ref().unwrap().data;
assert_eq!(tag.r#type, ResourceType::Tag);
}
}
}