libpixiv 0.1.2

pixiv.net API client library
Documentation
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;

/// Some comment on content 
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Comment {
    pub id: i64,
    pub comment: String,
    pub date: String,
    pub user: User,
    pub has_replies: bool,
    pub stamp: Option<Stamp>,
}

/// Some comment stamp
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Stamp {
    pub stamp_id: i64,
    pub stamp_url: String,
}

/// Some pixiv novel
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Novel {
    pub id: i64,
    pub title: String,
    pub caption: String,
    pub restrict: i64,
    pub x_restrict: i64,
    pub is_original: bool,
    pub image_urls: IllustrationURLs,
    pub create_date: String,
    pub tags: Vec<Tag>,
    pub page_count: i64,
    pub text_length: i64,
    pub user: User,
    #[serde(deserialize_with = "Novel::deserialize_series")]
    pub series: Option<Series>,
    pub is_bookmarked: bool,
    pub total_bookmarks: i64,
    pub total_view: i64,
    pub visible: bool,
    pub total_comments: i64,
    pub is_muted: bool,
    pub is_mypixiv_only: bool,
    pub is_x_restricted: bool,
    pub novel_ai_type: i64,
    pub request: Value,
}

// based on https://users.rust-lang.org/t/serde-deserialize-empty-string-as-option-none/116201/2
impl Novel {
    fn deserialize_series<'de, D>(d: D) -> Result<Option<Series>, D::Error>
    where
        D: Deserializer<'de>,
    {
        #[derive(Deserialize)]
        #[serde(untagged)]
        enum Helper {
            R(Series),
            #[allow(dead_code)] // value is still required for "validation"
            S(Value),
        }

        match Helper::deserialize(d) {
            Ok(Helper::S(_)) => Ok(None),
            Ok(Helper::R(s)) => Ok(Some(s)),
            Err(e) => Err(e),
        }
    }
}

/// Some pixiv user response.
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PixivUser {
    pub id: i64,
    pub name: String,
    pub account: String,
    pub profile_image_urls: ProfileImageUrls,
    pub comment: Option<String>,
    pub is_followed: bool,
    pub is_access_blocking_user: Option<bool>,
}

/// Some user profile image. 
///
/// Only one resolution is currently provided by the API.
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ProfileImageUrls {
    pub medium: String,
}

/// Some full user profile
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Profile {
    pub webpage: Value,
    pub gender: String,
    pub birth: String,
    pub birth_day: String,
    pub birth_year: i64,
    pub region: String,
    pub address_id: i64,
    pub country_code: String,
    pub job: String,
    pub job_id: i64,
    pub total_follow_users: i64,
    pub total_mypixiv_users: i64,
    pub total_illusts: i64,
    pub total_manga: i64,
    pub total_novels: i64,
    pub total_illust_bookmarks_public: i64,
    pub total_illust_series: i64,
    pub total_novel_series: i64,
    pub background_image_url: String,
    pub twitter_account: String,
    pub twitter_url: String,
    pub pawoo_url: Value,
    pub is_premium: bool,
    pub is_using_custom_profile_image: bool,
}

/// Private data provided by some user.
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ProfilePublicity {
    pub gender: String,
    pub region: String,
    pub birth_day: String,
    pub birth_year: String,
    pub job: String,
    pub pawoo: bool,
}

/// Some user's workspace metadata.
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Workspace {
    pub pc: String,
    pub monitor: String,
    pub tool: String,
    pub scanner: String,
    pub tablet: String,
    pub mouse: String,
    pub printer: String,
    pub desktop: String,
    pub music: String,
    pub desk: String,
    pub chair: String,
    pub comment: String,
    pub workspace_image_url: Value,
}

/// Some ugoira metadata. 
///
/// Ugoiras are pixivs "lossless" GIFs provided as zipped image sequences.
/// There is currently no helper that handles GIF encoding, so you will need 
/// to do that yourself for now.
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct UgoiraMetadata {
    pub zip_urls: ZipUrls,
    pub frames: Vec<Frame>,
}

/// Wrapper type for some zip url used for ugoiras.
///
/// There is currently only one ugoira resoluton is available.
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ZipUrls {
    pub medium: String,
}

/// An ugoira frame. 
///
/// Yes, the delay may differ per frame and its thus a good idea to not only check 
/// the first frame when building the Ugoira.
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Frame {
    pub file: String,
    pub delay: i64,
}

/// An Illustration
///
/// This struct holds most of the available data.
///
/// Some of the properties like `restrict` are not yet mapped to some enum,
/// those will be breaking changes and thus only introduced during major releases.
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Illustration {
    pub id: u32,
    pub tags: Vec<Tag>,
    pub visible: bool,
    pub r#type: String,
    pub title: String,
    pub caption: String,
    pub height: u32,
    pub width: u32,
    pub page_count: i32,
    pub user: User,
    pub tools: Vec<String>,
    pub series: Option<Series>,
    pub restrict: i32,
    pub x_restrict: i32,
    pub image_urls: IllustrationURLs,
    pub meta_single_page: Option<IllustrationMetaURL>,
    pub meta_pages: Vec<IllustrationURLsWrapper>,
    pub total_view: u32,
    pub total_bookmarks: u32,
    pub is_bookmarked: bool,
    pub is_muted: bool,
    pub total_comments: Option<u32>,
    pub illust_ai_type: u32,
    pub illust_book_style: u32,
    pub comment_access_control: Option<u32>,
    pub create_date: String,
}

/// Some illustration series.
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Series {
    pub id: u32,
    pub title: String,
}

/// Some user. 
///
/// While this is a smaller version of the `PixivUser` that is returned when calling the 
/// user specific endpoints, this will be returned on other endpoints like for illustrations.
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct User {
    pub id: i32,
    pub name: String,
    pub account: String,
    pub profile_image_urls: UserProfileImages,
    pub is_followed: Option<bool>,
}

/// Some user profile image 
///
/// There is currently only one resolution available.
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct UserProfileImages {
    pub medium: String,
}

/// Wrapper type for image urls.
///
/// Not for direct use, only for its content.
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct IllustrationURLsWrapper {
    pub image_urls: IllustrationURLs,
}

/// Wrapper type for the (optional) original image url
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct IllustrationMetaURL {
    pub original_image_url: Option<String>,
}

/// Collection of image urls
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct IllustrationURLs {
    pub square_medium: String,
    pub medium: String,
    pub large: String,
    pub original: Option<String>,
}

/// A tag 
///
/// Essentially a key value pair, the `translated_name` is not 
/// guarenteed to be filled at all, especially if the `Accept-Language`
/// header is missing.
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Tag {
    pub name: String,
    pub translated_name: Option<String>,
}

#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PixivUserResult {
    pub user: PixivUser,
    pub profile: Option<Profile>,
    pub profile_publicity: Option<ProfilePublicity>,
    pub workspace: Option<Workspace>,
}

/// umbrella type for API results
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PixivResult {
    pub comments: Option<Vec<Comment>>,
    pub illust: Option<Illustration>,
    pub illusts: Option<Vec<Illustration>>,
    pub error: Option<PixivError>,
    pub ugoira_metadata: Option<UgoiraMetadata>,
    pub user_previews: Option<Vec<PixivUserResult>>,
    pub tags: Option<Vec<Tag>>,
    pub novels: Option<Vec<Novel>>,
    pub users: Option<Vec<Value>>,
}

#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PixivError {
    user_message: String,
    message: String,
    reason: String,
}

impl std::error::Error for PixivError {}
impl std::fmt::Display for PixivError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}: {}", self.user_message, self.reason)
    }
}