268 lines
7.1 KiB
Rust
268 lines
7.1 KiB
Rust
use serde::de::{self, Visitor};
|
|
use serde::{Deserialize, Deserializer};
|
|
use std::collections::BTreeMap;
|
|
use std::fmt;
|
|
use std::marker::PhantomData;
|
|
use std::num::ParseIntError;
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
pub struct APIError {
|
|
pub error: String,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
pub struct GalleryTitleInfo {
|
|
pub english: Option<String>,
|
|
pub japanese: Option<String>,
|
|
pub pretty: Option<String>,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
pub struct GalleryImageInfo {
|
|
pub t: String,
|
|
pub w: i32,
|
|
pub h: i32,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
pub struct GalleryImagesInfo {
|
|
pub pages: Vec<GalleryImageInfo>,
|
|
pub cover: GalleryImageInfo,
|
|
pub thumbnail: GalleryImageInfo,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
pub struct GalleryTagInfo {
|
|
pub id: i32,
|
|
pub r#type: String,
|
|
pub name: String,
|
|
pub url: String,
|
|
pub count: i32,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
pub struct GalleryInfoSuccess {
|
|
#[serde(deserialize_with = "convert_to_i32")]
|
|
pub id: i32,
|
|
pub media_id: String,
|
|
pub title: GalleryTitleInfo,
|
|
pub images: GalleryImagesInfo,
|
|
pub scanlator: String,
|
|
pub upload_date: i32,
|
|
pub tags: Vec<GalleryTagInfo>,
|
|
pub num_pages: i32,
|
|
pub num_favorites: i32,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
#[serde(untagged)]
|
|
pub enum GalleryInfo {
|
|
Info(GalleryInfoSuccess),
|
|
Error(APIError),
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
pub struct RelatedGalleriesSuccess {
|
|
pub result: Vec<GalleryInfoSuccess>,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
#[serde(untagged)]
|
|
pub enum RelatedGalleries {
|
|
Galleries(RelatedGalleriesSuccess),
|
|
Error(APIError),
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct MiniGalleryInfo {
|
|
pub id: i32,
|
|
pub title: String,
|
|
}
|
|
|
|
impl fmt::Display for GalleryInfoSuccess {
|
|
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let mut text = format!("Sauce: {}\nTitle: ", self.id);
|
|
let japanese_title = self.title.japanese.as_ref();
|
|
let english_title = self.title.english.as_ref();
|
|
if english_title.filter(|i| !i.is_empty()).is_some() {
|
|
text.push_str(english_title.unwrap());
|
|
if japanese_title.filter(|i| !i.is_empty()).is_some() {
|
|
text.push_str(&format!("\nJapanese Title: {}", japanese_title.unwrap()));
|
|
}
|
|
} else {
|
|
text.push_str(japanese_title.unwrap());
|
|
}
|
|
let mut tag_hashmap = BTreeMap::new();
|
|
for tag_info in &self.tags {
|
|
let tag_key = tag_info.r#type.as_str();
|
|
let tag_value = tag_info.name.as_str();
|
|
let tag_vec = tag_hashmap.entry(tag_key).or_insert(Vec::new());
|
|
tag_vec.push(tag_value);
|
|
}
|
|
for (tag_key, tag_value) in tag_hashmap {
|
|
let tag_key = match tag_key {
|
|
"tag" => "Tags",
|
|
"artist" => "Artists",
|
|
"parody" => "Parodies",
|
|
"character" => "Characters",
|
|
"group" => "Groups",
|
|
"language" => "Languages",
|
|
"category" => "Categories",
|
|
_ => tag_key,
|
|
};
|
|
text.push_str(&format!("\n{}: {}", tag_key, tag_value.join(", ")));
|
|
}
|
|
text.push_str(&format!(
|
|
"\nPages: {}\nFavorites: {}",
|
|
self.num_pages, self.num_favorites
|
|
));
|
|
formatter.write_str(&text)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum Error {
|
|
Reqwest(reqwest::Error),
|
|
SerdeJSON(serde_json::Error),
|
|
QuickXML(quick_xml::Error),
|
|
ParseInt(ParseIntError),
|
|
Unknown(&'static str),
|
|
}
|
|
|
|
impl From<reqwest::Error> for Error {
|
|
#[inline]
|
|
fn from(error: reqwest::Error) -> Error {
|
|
Error::Reqwest(error)
|
|
}
|
|
}
|
|
|
|
impl From<serde_json::Error> for Error {
|
|
#[inline]
|
|
fn from(error: serde_json::Error) -> Error {
|
|
Error::SerdeJSON(error)
|
|
}
|
|
}
|
|
|
|
impl From<quick_xml::Error> for Error {
|
|
#[inline]
|
|
fn from(error: quick_xml::Error) -> Error {
|
|
Error::QuickXML(error)
|
|
}
|
|
}
|
|
|
|
impl From<ParseIntError> for Error {
|
|
#[inline]
|
|
fn from(error: ParseIntError) -> Error {
|
|
Error::ParseInt(error)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Error {
|
|
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let str = match self {
|
|
Error::Reqwest(err) => format!("reqwest error: {}", err),
|
|
Error::SerdeJSON(err) => format!("serde_json error: {}", err),
|
|
Error::QuickXML(err) => format!("quick_xml error: {}", err),
|
|
Error::ParseInt(err) => format!("parse int error: {}", err),
|
|
Error::Unknown(err) => err.to_string(),
|
|
};
|
|
formatter.write_str(&str)
|
|
}
|
|
}
|
|
|
|
fn convert_to_i32<'de, D>(deserializer: D) -> Result<i32, D::Error>
|
|
where
|
|
D: Deserializer<'de>,
|
|
{
|
|
struct ConvertToI32<T>(PhantomData<fn() -> T>);
|
|
|
|
impl<'de> Visitor<'de> for ConvertToI32<i32> {
|
|
type Value = i32;
|
|
|
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
formatter.write_str("an integer between -2^31 and 2^31")
|
|
}
|
|
|
|
fn visit_i8<E>(self, value: i8) -> Result<Self::Value, E>
|
|
where
|
|
E: de::Error,
|
|
{
|
|
Ok(i32::from(value))
|
|
}
|
|
|
|
fn visit_i16<E>(self, value: i16) -> Result<Self::Value, E>
|
|
where
|
|
E: de::Error,
|
|
{
|
|
Ok(i32::from(value))
|
|
}
|
|
|
|
fn visit_i32<E>(self, value: i32) -> Result<Self::Value, E>
|
|
where
|
|
E: de::Error,
|
|
{
|
|
Ok(value)
|
|
}
|
|
|
|
fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
|
|
where
|
|
E: de::Error,
|
|
{
|
|
use std::i32;
|
|
if value >= i64::from(i32::MIN) && value <= i64::from(i32::MAX) {
|
|
Ok(value as i32)
|
|
} else {
|
|
Err(E::custom(format!("i32 out of range: {}", value)))
|
|
}
|
|
}
|
|
|
|
fn visit_u8<E>(self, value: u8) -> Result<Self::Value, E>
|
|
where
|
|
E: de::Error,
|
|
{
|
|
Ok(i32::from(value))
|
|
}
|
|
|
|
fn visit_u16<E>(self, value: u16) -> Result<Self::Value, E>
|
|
where
|
|
E: de::Error,
|
|
{
|
|
Ok(i32::from(value))
|
|
}
|
|
|
|
fn visit_u32<E>(self, value: u32) -> Result<Self::Value, E>
|
|
where
|
|
E: de::Error,
|
|
{
|
|
use std::{i32, u32};
|
|
if value <= i32::MAX as u32 {
|
|
Ok(value as i32)
|
|
} else {
|
|
Err(E::custom(format!("i32 out of range: {}", value)))
|
|
}
|
|
}
|
|
|
|
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
|
|
where
|
|
E: de::Error,
|
|
{
|
|
use std::{i32, u64};
|
|
if value <= i32::MAX as u64 {
|
|
Ok(value as i32)
|
|
} else {
|
|
Err(E::custom(format!("i32 out of range: {}", value)))
|
|
}
|
|
}
|
|
|
|
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
|
where
|
|
E: de::Error,
|
|
{
|
|
// https://brokenco.de/2020/08/03/serde-deserialize-with-string.html
|
|
value.parse::<i32>().map_err(serde::de::Error::custom)
|
|
}
|
|
}
|
|
|
|
deserializer.deserialize_any(ConvertToI32(PhantomData))
|
|
}
|