use std::{collections::HashMap, fmt::Display, str::FromStr};
use chrono::{DateTime, Utc};
use iri_string::types::{IriAbsoluteString, IriRelativeString, IriString};
use mime::Mime;
use serde::{de::Error, Deserialize, Serialize};
use uuid::Uuid;
pub use iri_string;
use crate::models::{author::Author, award::{Awarding, TopAwardedType}, color::Color, comment::Comment, flair::{FlairTextColor, FlairType}, fullname::FullName, guilding::Gildings, media::{Media, MediaEmbed}, richtext::Richtext};
#[allow(clippy::struct_excessive_bools)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Link {
pub approved_at_utc: Option<DateTime<Utc>>,
pub selftext: String,
#[serde(deserialize_with = "crate::utils::bool_from_int")]
pub gilded: bool,
pub title: String,
pub pwls: Option<u32>,
#[serde(flatten)]
pub thumbnail: LinkThumbnail,
#[serde(flatten)]
pub post_link: PostLink,
#[serde(flatten)]
pub author: Author,
#[serde(flatten)]
pub flair: LinkFlair,
#[serde(flatten)]
pub subreddit_info: SubredditInfo,
#[serde(flatten)]
pub mod_info: ModInfo,
pub allow_live_comments: bool,
pub archived: bool,
pub can_gild: bool,
pub can_mod_post: bool,
pub clicked: bool,
pub contest_mode: bool,
pub hidden: bool,
pub hide_score: bool,
pub is_created_from_ads_ui: bool,
pub is_crosspostable: bool,
pub is_meta: bool,
pub is_original_content: bool,
pub is_reddit_media_domain: bool,
pub is_robot_indexable: bool,
pub is_self: bool,
pub is_video: bool,
pub locked: bool,
pub no_follow: bool,
pub over_18: bool,
pub pinned: bool,
pub quarantine: bool,
pub saved: bool,
pub send_replies: bool,
pub spoiler: bool,
pub stickied: bool,
pub visited: bool,
pub name: FullName,
pub ups: u32,
pub downs: u32,
pub upvote_ratio: f32,
pub total_awards_received: u32,
pub score: u32,
pub num_comments: u32,
pub num_crossposts: u32,
pub media_metadata: Option<MediaMetadatas>,
pub media_only: bool,
pub media: Option<Media>,
#[serde(deserialize_with = "crate::utils::object_empty_as_none")]
pub media_embed: Option<MediaEmbed>,
pub secure_media: Option<Media>,
#[serde(deserialize_with = "crate::utils::object_empty_as_none")]
pub secure_media_embed: Option<MediaEmbed>,
pub user_reports: Vec<UserReport>,
pub category: Option<()>,
pub approved_by: Option<()>,
#[serde(deserialize_with = "crate::utils::false_or_datetime")]
pub edited: Option<DateTime<Utc>>,
pub gildings: Gildings,
pub content_categories: Option<Vec<ContentCategories>>,
pub wls: Option<u32>,
pub removed_by_category: Option<()>,
pub banned_by: Option<()>,
pub domain: String,
pub selftext_html: Option<String>,
pub likes: Option<()>,
pub suggested_sort: Option<Sort>,
pub banned_at_utc: Option<()>,
pub view_count: Option<()>,
pub all_awardings: Vec<Awarding>,
pub awarders: Vec<()>,
pub top_awarded_type: Option<TopAwardedType>,
pub treatment_tags: Vec<()>,
pub removed_by: Option<()>,
pub num_reports: Option<()>,
pub distinguished: Option<Distinguished>,
pub removal_reason: Option<()>,
pub id: String,
pub report_reasons: Option<()>,
pub discussion_type: Option<()>,
pub whitelist_status: Option<WhitelistStatus>,
pub parent_whitelist_status: Option<WhitelistStatus>,
pub permalink: IriRelativeString,
#[serde(deserialize_with = "crate::utils::integer_or_float_to_datetime")]
pub created: DateTime<Utc>,
#[serde(deserialize_with = "crate::utils::integer_or_float_to_datetime")]
pub created_utc: DateTime<Utc>,
pub post_hint: Option<PostHint>,
pub preview: Option<Preview>,
pub url_overridden_by_dest: Option<IriString>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum Distinguished {
Moderator,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct MediaMetadatas(HashMap<MediaMetadataId, MediaMetadata>);
#[derive(Debug, Clone, Serialize, PartialEq, Hash, Eq, PartialOrd, Ord)]
pub struct MediaMetadataId(u128);
impl<'de> Deserialize<'de> for MediaMetadataId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let id = String::deserialize(deserializer)
.map(|s| u128::from_str_radix(&s, 36).map_err(D::Error::custom))??;
Ok(Self(id))
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct MediaMetadata {
status: MediaMetadataStatus,
#[serde(rename = "e")]
ty: MediaMetadataType,
#[serde(rename = "m", with = "crate::utils::mime_serde")]
mime_type: Mime,
#[serde(rename = "p")]
p: Vec<P>,
#[serde(rename = "s")]
s: P,
#[serde(rename = "id")]
id: MediaMetadataId,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct P {
y: u16,
x: u16,
u: IriString,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum MediaMetadataType {
Image,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum MediaMetadataStatus {
Valid,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
pub enum PostLink {
Url {
url: IriString,
},
Crosspost {
#[serde(rename = "crosspost_parent")]
parent: FullName,
#[serde(rename = "crosspost_parent_list")]
list: Vec<Link>,
url: IriRelativeString,
},
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ModInfo {
#[serde(rename = "mod_reason_title")]
pub reason_title: Option<String>,
#[serde(rename = "mod_reports")]
pub reports: Vec<()>,
#[serde(rename = "mod_note")]
pub note: Option<()>,
#[serde(rename = "mod_reason_by")]
pub reason_by: Option<()>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct SubredditInfo {
#[serde(rename = "subreddit")]
pub name: String,
#[serde(rename = "subreddit_name_prefixed")]
pub name_prefixed: String,
#[serde(rename = "subreddit_type")]
pub ty: SubredditType,
#[serde(rename = "subreddit_subscribers")]
pub subscribers: u32,
#[serde(rename = "subreddit_id")]
pub id: FullName,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
pub struct LinkFlairTemplateId(Uuid);
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct LinkFlair {
#[serde(deserialize_with = "Color::from_hex_str")]
#[serde(rename = "link_flair_background_color")]
pub background_color: Option<Color>,
#[serde(rename = "link_flair_template_id")]
pub template_id: Option<LinkFlairTemplateId>,
#[serde(rename = "link_flair_text_color")]
pub text_color: FlairTextColor,
#[serde(rename = "link_flair_text")]
pub text: Option<String>,
#[serde(rename = "link_flair_type")]
pub ty: FlairType,
#[serde(rename = "link_flair_richtext")]
pub richtext: Vec<Richtext>,
#[serde(rename = "link_flair_css_class")]
pub css_class: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct LinkThumbnail {
#[serde(rename = "thumbnail")]
pub link: LinkThumbnailType,
#[serde(rename = "thumbnail_height")]
pub height: Option<u16>,
#[serde(rename = "thumbnail_width")]
pub width: Option<u16>,
}
#[derive(Debug, Clone, Serialize, PartialEq)]
#[serde(untagged)]
pub enum LinkThumbnailType {
This,
Default,
Nsfw,
Spoiler,
Url(IriAbsoluteString),
}
impl FromStr for LinkThumbnailType {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"self" => Ok(Self::This),
"default" => Ok(Self::Default),
"nsfw" => Ok(Self::Nsfw),
"spoiler" => Ok(Self::Spoiler),
_ => Ok(Self::Url(IriAbsoluteString::from_str(s).map_err(
|why| format!("invalid IRI or thumbnail type: {}, error: {}", s, why),
)?)),
}
}
}
impl<'de> Deserialize<'de> for LinkThumbnailType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
LinkThumbnailType::from_str(&String::deserialize(deserializer)?)
.map_err(serde::de::Error::custom)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum PostHint {
#[serde(rename = "image")]
Image,
#[serde(rename = "link")]
Link,
#[serde(rename = "gallery")]
Gallery,
#[serde(rename = "self")]
This,
#[serde(rename = "rich:video")]
RichVideo,
#[serde(rename = "hosted:video")]
HostedVideo,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Preview {
enabled: bool,
images: Vec<Images>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Images {
id: String,
resolutions: Vec<Image>,
source: Image,
variants: Variants,
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Variants {
obfuscated: Option<Variant>,
nsfw: Option<Variant>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Variant {
resolutions: Vec<Image>,
source: Image,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Image {
height: u16,
width: u16,
url: IriAbsoluteString,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum ContentCategories {
Photography,
DrawingAndPainting,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct UserReport;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "kind", content = "data")]
pub enum RedditListing {
Listing {
after: Option<FullName>,
dist: Option<u32>,
modhash: String,
geo_filter: Option<String>,
children: Vec<RedditListing>,
},
#[serde(rename = "t3")]
Link(Box<Link>),
#[serde(rename = "t1")]
Comment(Box<Comment>),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum Sort {
New,
Top,
Confidence,
}
impl Display for Sort {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Sort::New => "new",
Sort::Top => "top",
Sort::Confidence => "confidence",
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum SubredditType {
Public,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum WhitelistStatus {
AllAds,
SomeAds,
NoAds,
PromoAdultNsfw,
}