pixapi 0.1.1

An API for accessing to pixiv.net
Documentation
use std::collections::HashMap;

use serde::{Deserialize, Deserializer, Serialize};
use time::OffsetDateTime;

use super::tag::Tag;

#[allow(dead_code)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum AbstractSize {
    SquareMedium,
    Medium,
    Large,
    Original,
    Master,
    BigThumbnail,
    SquareMediumWebp, // Так то это 360x360, но качетсво хуже чем у SquareMedium
}

#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
pub struct Size {
    pub width: u32,
    pub height: u32,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Urls {
    pub square_medium: String,
    pub medium: String,
    // Ширина всегда 600, но если высота более чем в два раза больше ширины, то высота всегда 1200
    pub large: String,
}

//noinspection ALL
#[allow(non_snake_case)]
#[derive(Deserialize, Debug, Clone)]
pub struct Illust {
    pub id: u32,
    pub title: String,
    #[serde(rename(deserialize = "type"))]
    pub work_type: String,
    pub image_urls: Urls,
    pub caption: String,
    pub restrict: u32,
    #[serde(with = "crate::models::tag")]
    pub tags: Vec<Tag>,
    #[serde(with = "time::serde::rfc3339")]
    pub create_date: OffsetDateTime,
    #[serde(flatten)]
    pub size: Size,
    pub page_count: u32,
    pub sanity_level: u32,
    pub x_restrict: u32,
    #[serde(with = "Urls")]
    pub meta_pages: Vec<Urls>,
    pub total_view: u32,
    pub total_bookmarks: u32,
    pub is_bookmarked: bool,
    pub visible: bool,
    pub is_muted: bool,
    pub illust_ai_type: u32,
}

impl Illust {
    pub fn get_meta_pages(&self) -> Vec<Illust> {
        let mut v = vec![];
        for i in &self.meta_pages {
            let mut a = self.clone();
            a.image_urls = i.clone();
            a.meta_pages.clear();
            v.push(a);
        }
        v
    }
}

impl Size {
    pub fn unknown() -> Size {
        Size {
            width: 0,
            height: 0,
        }
    }

    pub fn master(&self) -> Size {
        if self.width == 0 || self.height == 0 {
            return Size {
                width: 1200,
                height: 600,
            };
        }
        if self.width <= 1200 && self.height <= 1200 {
            return self.clone();
        }
        if self.width > self.height {
            Size {
                width: 1200,
                height: (1200.0 * self.height as f32 / self.width as f32) as u32,
            }
        } else {
            Size {
                height: 1200,
                width: (1200.0 * self.width as f32 / self.height as f32) as u32,
            }
        }
    }

    pub fn large(&self) -> Size {
        if self.width == 0 || self.height == 0 {
            return Size {
                width: 600,
                height: 600,
            };
        }
        if self.height > 2 * self.width {
            Size {
                height: 1200,
                width: (1200.0 * self.width as f32 / self.height as f32) as u32,
            }
        } else {
            Size {
                width: 600,
                height: (600.0 * self.height as f32 / self.width as f32) as u32,
            }
        }
    }

    pub fn medium(&self) -> Size {
        if self.width == 0 || self.height == 0 {
            return Size {
                width: 540,
                height: 540,
            };
        }
        if self.width > self.height {
            Size {
                width: 540,
                height: (540.0 * self.height as f32 / self.width as f32) as u32,
            }
        } else {
            Size {
                height: 540,
                width: (540.0 * self.width as f32 / self.height as f32) as u32,
            }
        }
    }
}

impl Into<[usize; 2]> for Size {
    fn into(self) -> [usize; 2] {
        [self.width as usize, self.height as usize]
    }
}

impl Urls {
    pub fn original(&self) -> String {
        self.square_medium
            .replace("c/360x360_70/", "")
            .replace("img-master", "img-original")
            .replace("_square1200", "")
    }

    // У master одна сторона 1200, она же наибольшая
    pub fn master(&self) -> String {
        self.square_medium
            .replace("c/360x360_70/", "")
            .replace("_square1200", "_master1200")
    }

    // pub fn illust_id(&self) -> String {
    //     let end = self.square_medium.find("_p").unwrap();
    //     let start = self.square_medium.rfind('/').unwrap();
    //     self.square_medium[start..=end].to_string()
    // }

    pub fn square_medium_webp(&self) -> String {
        self.square_medium
            .replacen("c/360x360_70/", "c/360x360_10_webp/", 1)
    }

    pub fn by_abstract_size(&self, abstract_size: AbstractSize) -> String {
        match abstract_size {
            AbstractSize::SquareMedium => self.square_medium.to_string(),
            AbstractSize::Medium => self.medium.to_string(),
            AbstractSize::Large => self.large.to_string(),
            AbstractSize::Original => self.original(),
            AbstractSize::Master => self.master(),
            AbstractSize::BigThumbnail => self.square_medium.to_string(),
            AbstractSize::SquareMediumWebp => self.square_medium_webp(),
        }
    }

    pub fn deserialize<'de, D>(d: D) -> Result<Vec<Urls>, D::Error>
    where
        D: Deserializer<'de>,
    {
        let a = <Vec<HashMap<String, Urls>>>::deserialize(d);
        match a {
            Ok(a) => Ok(a
                .into_iter()
                .map(|x| x.into_values().next().unwrap())
                .collect()),
            Err(err) => Err(err),
        }
    }
}