use std::{collections::HashMap, path::Path};
use url::Url;
#[derive(Debug, serde::Deserialize)]
pub struct Deviation {
#[serde(rename = "blockReasons")]
pub block_reasons: Vec<serde_json::Value>,
#[serde(rename = "deviationId")]
pub deviation_id: u64,
#[serde(rename = "type")]
pub kind: String,
pub url: Url,
pub media: DeviationMedia,
pub title: String,
#[serde(rename = "textContent")]
pub text_content: Option<TextContext>,
#[serde(rename = "isDownloadable")]
pub is_downloadable: bool,
#[serde(flatten)]
pub unknown: HashMap<String, serde_json::Value>,
}
impl Deviation {
pub fn get_media_url(&self) -> Option<Url> {
let mut url = self.media.base_uri.as_ref()?.clone();
url.query_pairs_mut()
.append_pair("token", self.media.token.first()?);
Some(url)
}
pub fn get_download_url(&self) -> Option<Url> {
let mut url = self.media.base_uri.as_ref()?.clone();
url.query_pairs_mut()
.append_pair("token", self.media.token.get(1)?);
Some(url)
}
pub fn get_fullview_url(&self) -> Option<Url> {
let mut url = self.media.base_uri.as_ref()?.clone();
if let Some(path) = self.media.get_fullview_media_type()?.content.as_ref() {
let mut path_segments_mut = url.path_segments_mut().ok()?;
for path in path.split('/').filter(|p| !p.is_empty()) {
let pretty_name = self.media.pretty_name.as_ref()?;
let path = path.replace("<prettyName>", pretty_name);
path_segments_mut.push(&path);
}
}
if let Some(token) = self.media.token.first() {
url.query_pairs_mut().append_pair("token", token);
}
Some(url)
}
pub fn get_gif_url(&self) -> Option<Url> {
let mut url = self.media.get_gif_media_type()?.b.clone()?;
url.query_pairs_mut()
.append_pair("token", self.media.token.first()?);
Some(url)
}
pub fn get_best_video_url(&self) -> Option<&Url> {
let url = self.media.get_best_video_media_type()?.b.as_ref()?;
Some(url)
}
pub fn is_image(&self) -> bool {
self.kind == "image"
}
pub fn is_literature(&self) -> bool {
self.kind == "literature"
}
pub fn is_film(&self) -> bool {
self.kind == "film"
}
pub fn get_image_download_url(&self) -> Option<Url> {
if let Some(url) = self.get_download_url() {
return Some(url);
}
if let Some(url) = self.get_gif_url() {
return Some(url);
}
None
}
pub fn get_extension(&self) -> Option<&str> {
if self.is_image() {
let url = self
.media
.get_gif_media_type()
.and_then(|media_type| media_type.b.as_ref())
.or(self.media.base_uri.as_ref())?;
Path::new(url.as_str()).extension()?.to_str()
} else if self.is_literature() {
None
} else if self.is_film() {
let url = self.media.get_best_video_media_type()?.b.as_ref()?;
Path::new(url.as_str()).extension()?.to_str()
} else {
None
}
}
}
#[derive(Debug, serde::Deserialize)]
pub struct Author {
#[serde(rename = "isNewDeviant")]
pub is_new_deviant: bool,
#[serde(rename = "useridUuid")]
pub userid_uuid: String,
pub usericon: Url,
#[serde(rename = "userId")]
pub user_id: u64,
pub username: String,
#[serde(flatten)]
pub unknown: HashMap<String, serde_json::Value>,
}
#[derive(Debug, serde::Deserialize)]
pub struct DeviationMedia {
#[serde(rename = "baseUri")]
pub base_uri: Option<Url>,
#[serde(default)]
pub token: Vec<String>,
pub types: Vec<MediaType>,
#[serde(rename = "prettyName")]
pub pretty_name: Option<String>,
#[serde(flatten)]
pub unknown: HashMap<String, serde_json::Value>,
}
impl DeviationMedia {
pub fn get_fullview_media_type(&self) -> Option<&MediaType> {
self.types.iter().find(|t| t.is_fullview())
}
pub fn get_gif_media_type(&self) -> Option<&MediaType> {
self.types.iter().find(|t| t.is_gif())
}
pub fn get_best_video_media_type(&self) -> Option<&MediaType> {
self.types
.iter()
.filter(|media_type| media_type.is_video())
.max_by_key(|media_type| media_type.width)
}
}
#[derive(Debug, serde::Deserialize)]
pub struct MediaType {
#[serde(rename = "c")]
pub content: Option<String>,
#[serde(rename = "h")]
pub height: u64,
#[serde(rename = "t")]
pub kind: String,
#[serde(rename = "w")]
pub width: u64,
pub b: Option<Url>,
#[serde(flatten)]
pub unknown: HashMap<String, serde_json::Value>,
}
impl MediaType {
pub fn is_fullview(&self) -> bool {
self.kind == "fullview"
}
pub fn is_gif(&self) -> bool {
self.kind == "gif"
}
pub fn is_video(&self) -> bool {
self.kind == "video"
}
}
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct TextContext {
pub excerpt: String,
pub html: Html,
#[serde(flatten)]
pub unknown: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct Html {
pub features: String,
pub markup: Option<String>,
#[serde(rename = "type")]
pub kind: String,
#[serde(flatten)]
pub unknown: HashMap<String, serde_json::Value>,
}
impl Html {
pub fn get_markup(&self) -> Option<Result<Markup, serde_json::Error>> {
let markup = self.markup.as_ref()?;
Some(serde_json::from_str(markup))
}
}
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct Markup {
pub blocks: Vec<Block>,
#[serde(rename = "entityMap")]
pub entity_map: serde_json::Value,
#[serde(flatten)]
pub unknown: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct Block {
pub data: serde_json::Value,
pub depth: u64,
pub key: String,
pub text: String,
#[serde(rename = "type")]
pub kind: String,
#[serde(flatten)]
pub unknown: HashMap<String, serde_json::Value>,
}