refactor!: rewrote all npf types and remove old npf code

This commit is contained in:
Gustavo "Guz" L. de Mello
2024-04-22 16:05:52 -03:00
parent 8a595d78f3
commit 0e916be2e0
4 changed files with 578 additions and 447 deletions

73
Cargo.lock generated
View File

@@ -215,6 +215,18 @@ dependencies = [
"windows-sys 0.42.0",
]
[[package]]
name = "color-art"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "097466279436dfec57a8d00b9abf55df1496a326e01b57918d5fd776598ad91f"
dependencies = [
"lazy_static",
"rand",
"serde",
"thiserror",
]
[[package]]
name = "colorchoice"
version = "1.0.0"
@@ -436,6 +448,17 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "getrandom"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@@ -553,6 +576,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.153"
@@ -589,6 +618,7 @@ version = "0.1.0"
dependencies = [
"clap",
"clio",
"color-art",
"comrak",
"serde",
"serde_json",
@@ -687,6 +717,12 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.79"
@@ -714,6 +750,36 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "regex"
version = "1.10.4"
@@ -1054,6 +1120,7 @@ dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
"serde",
]
[[package]]
@@ -1072,6 +1139,12 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.92"

View File

@@ -8,9 +8,10 @@ edition = "2021"
[dependencies]
clap = "4.5.3"
clio = { version = "0.3.5", features = ["clap-parse"] }
color-art = "0.3.8"
comrak = "0.21.0"
serde = { version = "1.0.197", features = ["derive"] }
serde_json = "1.0.114"
serde_with = { version = "3.7.0", features = [ "macros" ] }
serde_yaml = "0.9.34"
url = "2.5.0"
url = { version = "2.5.0", features = ["serde"] }

View File

@@ -1,187 +1,3 @@
use std::{cell::RefCell, fmt::Error};
pub mod npf;
use comrak::{arena_tree::Node, nodes::Ast, nodes::NodeValue};
mod npf;
use crate::utils;
use npf::content_types::text::{Formatting, FormattingType, Subtypes};
use npf::{content_types, objects::Media, ContentType, NPF};
#[derive(clap::ValueEnum, Clone, Debug)]
pub enum Formats {
TumblrNPF,
}
pub fn to_tumblr_npf<'a>(ast: &'a Node<'a, RefCell<Ast>>) -> Result<RefCell<NPF>, Error> {
let npf = RefCell::new(NPF::<'a>::new());
utils::iter_nodes_shallow(ast, &|node| match &node.data.borrow().value {
NodeValue::Paragraph => {
let text = RefCell::new(String::new());
let formatting = RefCell::new(Vec::<Formatting>::new());
utils::iter_nodes_shallow(node, &|node| {
let mut text = text.borrow_mut();
let mut formatting = formatting.borrow_mut();
match &node.data.borrow().value {
NodeValue::Link(l) => {
let t = utils::extract_text(node);
formatting.push(Formatting {
r#type: FormattingType::Link,
start: text.chars().count(),
end: text.chars().count() + t.chars().count() + 1,
url: Some(String::from(&l.url)),
color: None,
blog: None,
});
text.push_str(&format!("{} ", &t))
}
NodeValue::Strong => {
let t = utils::extract_text(node);
formatting.push(Formatting {
r#type: FormattingType::Bold,
start: text.chars().count(),
end: text.chars().count() + t.chars().count() + 1,
url: None,
color: None,
blog: None,
});
text.push_str(&format!("{} ", &t))
}
NodeValue::Emph => {
let t = utils::extract_text(node);
formatting.push(Formatting {
r#type: FormattingType::Italic,
start: text.chars().count(),
end: text.chars().count() + t.chars().count() + 1,
url: None,
color: None,
blog: None,
});
text.push_str(&format!("{} ", &t))
}
NodeValue::Text(t) => text.push_str(&format!("{} ", &t)),
NodeValue::Image(i) => {
if let Ok(u) = url::Url::parse(&i.url) {
if [
Some("www.youtube.com"),
Some("youtube.com"),
Some("youtu.be"),
]
.contains(&u.host_str())
{
let mut video = content_types::Video::from(String::from(u));
video.provider = Some(String::from("youtube"));
video.embed_iframe = Some(npf::objects::IFrame {
url: String::from(&video.url.clone().unwrap()),
width: None,
height: None,
});
npf.borrow_mut().content.push(ContentType::Video(video));
}
} else {
let mut image = content_types::Image::from(vec![Media {
r#type: None,
url: String::from(&i.url),
provider: None,
poster: None,
width: None,
height: None,
has_original_dimensions: None,
cropped: None,
original_dimensions_missing: None,
}]);
image.caption = Some(String::from(&i.title));
image.alt_text = Some(utils::extract_text(node));
npf.borrow_mut().content.push(ContentType::Image(image));
}
}
_ => (),
};
Ok::<(), Error>(())
})?;
let text = text.borrow().trim().to_string().replace(" ", " ");
let mut block = content_types::Text::from(text);
block.formatting = if formatting.borrow().len() > 0 {
Some(formatting.borrow().to_vec())
} else {
None
};
npf.borrow_mut().content.push(ContentType::Text(block));
Ok::<(), Error>(())
}
NodeValue::BlockQuote => {
let block_quote = to_tumblr_npf(node)?;
let final_block = RefCell::new(content_types::Text::new());
block_quote.borrow_mut().content.iter_mut().for_each(|b| {
if let ContentType::Text(t) = b {
let mut fb = final_block.borrow_mut();
let text_len = fb.text.chars().count();
if let Some(formattings) = &t.formatting {
for formatting in formattings {
match fb.formatting {
Some(ref mut v) => v.push(Formatting {
start: text_len + formatting.start,
end: text_len + formatting.end,
..formatting.clone()
}),
None => {
fb.formatting = Some(vec![Formatting {
start: text_len + formatting.start,
end: text_len + formatting.end,
..formatting.clone()
}]);
}
}
}
}
fb.text.push_str(&format!("{}\n\n", &t.text));
} else {
}
});
Ok(())
}
NodeValue::Heading(h) => {
let mut text = content_types::Text::from(utils::extract_text(node));
if h.level == 1 {
text.subtype = Some(Subtypes::Heading1);
} else if h.level == 2 {
text.subtype = Some(Subtypes::Heading2);
} else if h.level == 3 {
text.subtype = Some(Subtypes::UnorderedListItem);
let formatting = Formatting {
r#type: FormattingType::Bold,
start: 0,
end: text.text.chars().count() + 1,
url: None,
color: None,
blog: None,
};
match text.formatting {
Some(ref mut f) => f.push(formatting),
None => text.formatting = Some(vec![formatting]),
};
} else {
text.subtype = Some(Subtypes::UnorderedListItem);
}
npf.borrow_mut().content.push(ContentType::Text(text));
Ok(())
}
_ => Ok(()),
})?;
Ok(npf)
}
fn test() {}

View File

@@ -1,170 +1,142 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize)]
pub struct NPF<'a> {
#[serde(borrow)]
pub content: Vec<ContentType<'a>>,
pub struct NPF {
pub content: Vec<blocks::BlockValue>,
}
impl<'a> NPF<'a> {
pub fn new() -> NPF<'a> {
NPF { content: vec![] }
}
}
pub mod blocks {
use std::{collections::HashMap, str::FromStr};
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(untagged)]
pub enum ContentType<'a> {
#[serde(borrow)]
Text(content_types::Text<'a>),
#[serde(borrow)]
Link(content_types::Link<'a>),
#[serde(borrow)]
Audio(content_types::Audio<'a>),
#[serde(borrow)]
Image(content_types::Image<'a>),
#[serde(borrow)]
Video(content_types::Video<'a>),
}
pub mod content_types {
use super::objects;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub mod text {
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Subtypes {
Heading1,
Heading2,
Quirky,
Quote,
Indented,
Chat,
OrderedListItem,
UnorderedListItem,
}
use super::{attributions, objects};
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Formatting {
pub start: usize,
pub end: usize,
pub r#type: FormattingType,
pub url: Option<String>,
pub blog: Option<super::objects::Blog>,
pub color: Option<String>,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum BlockValue {
Text(BlockText),
Image(BlockImage),
Link(BlockLink),
Audio(BlockAudio),
Video(BlockVideo),
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum FormattingType {
Bold,
Italic,
Small,
Strikethrough,
Link,
Mention,
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum BlockTextSubtype {
Heading1,
Heading2,
Quirky,
Quote,
Indented,
Chat,
OrderedListItem,
UnordoredListItem,
}
trait BlockType: Default {
fn new() -> Self {
Self::default()
}
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Text<'a> {
r#type: &'a str,
#[derive(Debug, Deserialize, Serialize)]
pub struct BlockText {
r#type: String,
pub subtype: Option<BlockTextSubtype>,
pub text: String,
pub subtype: Option<text::Subtypes>,
pub indent_level: Option<u8>,
pub formatting: Option<Vec<text::Formatting>>,
pub formatting: Option<Vec<super::text_formatting::FormatValue>>,
pub ident_level: Option<u8>,
}
impl<'a> Text<'a> {
pub fn new() -> Text<'a> {
Text {
r#type: "text",
impl Default for BlockText {
fn default() -> Self {
Self {
r#type: String::from("text"),
subtype: None,
text: String::new(),
subtype: None,
indent_level: None,
formatting: None,
}
}
pub fn from(str: String) -> Text<'a> {
Text {
r#type: "text",
text: str,
subtype: None,
indent_level: None,
formatting: None,
ident_level: None,
}
}
}
impl From<String> for BlockText {
fn from(value: String) -> Self {
Self {
text: value,
..Self::default()
}
}
}
impl From<&str> for BlockText {
fn from(value: &str) -> Self {
Self {
text: String::from(value),
..Self::default()
}
}
}
impl BlockType for BlockText {}
#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Image<'a> {
r#type: &'a str,
#[derive(Debug, Deserialize, Serialize)]
pub struct BlockImage {
r#type: String,
pub media: Vec<objects::Media>,
pub colors: Option<HashMap<String, String>>,
pub feedback_token: Option<String>,
pub poster: Option<objects::Media>,
pub attribution: Option<attributions::AttributionValue>,
pub alt_text: Option<String>,
pub caption: Option<String>,
pub feedback_token: Option<String>,
pub colors: Option<HashMap<String, String>>,
pub attribution: Option<objects::Attribution>,
}
impl<'a> Image<'a> {
pub fn new() -> Image<'a> {
Image {
r#type: "image",
impl Default for BlockImage {
fn default() -> Self {
Self {
r#type: String::from("image"),
media: vec![],
colors: None,
feedback_token: None,
poster: None,
attribution: None,
alt_text: None,
caption: None,
feedback_token: None,
colors: None,
attribution: None,
}
}
pub fn from(media: Vec<objects::Media>) -> Image<'a> {
Image {
media,
r#type: "image",
alt_text: None,
caption: None,
feedback_token: None,
colors: None,
attribution: None,
}
}
}
impl From<Vec<objects::Media>> for BlockImage {
fn from(value: Vec<objects::Media>) -> Self {
Self {
media: value,
..Self::default()
}
}
}
impl From<objects::Media> for BlockImage {
fn from(value: objects::Media) -> Self {
Self::from(vec![value])
}
}
impl BlockType for BlockImage {}
#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Link<'a> {
r#type: &'a str,
pub url: String,
#[derive(Debug, Deserialize, Serialize)]
pub struct BlockLink {
r#type: String,
pub url: url::Url,
pub title: Option<String>,
pub description: Option<String>,
pub author: Option<String>,
pub site_name: Option<String>,
pub display_url: Option<String>,
pub poster: Option<Vec<objects::Media>>,
pub display_url: Option<url::Url>,
pub poster: Option<objects::Media>,
}
impl<'a> Link<'a> {
pub fn new() -> Link<'a> {
Link {
r#type: "link",
url: String::new(),
title: None,
description: None,
author: None,
site_name: None,
display_url: None,
poster: None,
}
}
pub fn from(url: String) -> Link<'a> {
Link {
r#type: "link",
url,
impl Default for BlockLink {
fn default() -> Self {
Self {
r#type: String::from("link"),
url: url::Url::from_str("https://tumblr.com").unwrap(),
title: None,
description: None,
author: None,
@@ -174,194 +146,463 @@ pub mod content_types {
}
}
}
impl From<url::Url> for BlockLink {
fn from(value: url::Url) -> Self {
Self {
url: value,
..Self::default()
}
}
}
impl BlockType for BlockLink {}
#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Audio<'a> {
r#type: &'a str,
pub url: Option<String>,
#[derive(Debug, Deserialize, Serialize)]
pub struct BlockAudio {
r#type: String,
pub media: Option<objects::Media>,
pub url: Option<url::Url>,
pub provider: Option<String>,
pub title: Option<String>,
pub artist: Option<String>,
pub album: Option<String>,
pub poster: Option<Vec<objects::Media>>,
pub poster: Option<objects::Media>,
pub embed_html: Option<String>,
pub embed_url: Option<String>,
pub metadata: Option<serde_json::Value>,
pub attribution: Option<objects::Attribution>,
pub embed_iframe: Option<objects::EmbedIframe>,
pub metadata: Option<HashMap<String, serde_json::Value>>,
pub attribution: Option<attributions::AttributionValue>,
}
impl<'a> Audio<'a> {
pub fn new() -> Audio<'a> {
Audio {
r#type: "audio",
url: Some(String::new()),
media: None,
provider: None,
title: None,
artist: None,
album: None,
poster: None,
embed_html: None,
embed_url: None,
metadata: None,
attribution: None,
impl BlockAudio {
pub fn is_valid(&self) -> bool {
if self.url.is_some() || self.media.is_some() {
true
} else {
false
}
}
pub fn from(url: String) -> Audio<'a> {
Audio {
r#type: "audio",
url: Some(url),
}
impl Default for BlockAudio {
fn default() -> Self {
Self {
r#type: String::from("audio"),
media: None,
provider: None,
title: None,
artist: None,
album: None,
poster: None,
embed_html: None,
embed_url: None,
metadata: None,
attribution: None,
}
}
pub fn from_media(media: objects::Media) -> Audio<'a> {
Audio {
r#type: "audio",
url: None,
media: Some(media),
provider: None,
title: None,
artist: None,
album: None,
poster: None,
embed_html: None,
embed_url: None,
embed_iframe: None,
metadata: None,
attribution: None,
}
}
}
impl From<objects::Media> for BlockAudio {
fn from(value: objects::Media) -> Self {
Self {
media: Some(value),
..Self::default()
}
}
}
impl From<url::Url> for BlockAudio {
fn from(value: url::Url) -> Self {
Self {
url: Some(value),
..Self::default()
}
}
}
impl BlockType for BlockAudio {}
#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Video<'a> {
r#type: &'a str,
#[derive(Debug, Deserialize, Serialize)]
pub struct BlockVideo {
r#type: String,
pub url: Option<url::Url>,
pub media: Option<objects::Media>,
pub url: Option<String>,
pub provider: Option<String>,
pub embed_html: Option<String>,
pub embed_iframe: Option<objects::IFrame>,
pub embed_url: Option<String>,
pub poster: Option<Vec<objects::Media>>,
pub filmstrip: Option<Vec<objects::Media>>,
pub metadata: Option<serde_json::Value>,
pub attribution: Option<objects::Attribution>,
pub embed_iframe: Option<objects::EmbedIframe>,
pub embed_url: Option<url::Url>,
pub poster: Option<objects::Media>,
pub filmstrip: Option<objects::Media>,
pub attribution: Option<attributions::AttributionValue>,
pub can_autoplay_on_cellular: Option<bool>,
}
impl<'a> Video<'a> {
pub fn new() -> Video<'a> {
Video {
r#type: "video",
url: Some(String::new()),
media: None,
provider: None,
embed_html: None,
embed_iframe: None,
embed_url: None,
poster: None,
filmstrip: None,
metadata: None,
attribution: None,
can_autoplay_on_cellular: None,
impl BlockVideo {
pub fn is_valid(&self) -> bool {
if self.url.is_some() || self.media.is_some() {
true
} else {
false
}
}
pub fn from(url: String) -> Video<'a> {
Video {
r#type: "video",
url: Some(url),
}
impl Default for BlockVideo {
fn default() -> Self {
Self {
r#type: String::from("audio"),
media: None,
provider: None,
embed_html: None,
embed_iframe: None,
embed_url: None,
poster: None,
filmstrip: None,
metadata: None,
attribution: None,
can_autoplay_on_cellular: None,
}
}
pub fn from_media(media: objects::Media) -> Video<'a> {
Video {
r#type: "video",
url: None,
media: Some(media),
provider: None,
embed_html: None,
embed_iframe: None,
embed_url: None,
poster: None,
filmstrip: None,
metadata: None,
attribution: None,
can_autoplay_on_cellular: None,
}
}
}
impl From<objects::Media> for BlockVideo {
fn from(value: objects::Media) -> Self {
Self {
media: Some(value),
..Self::default()
}
}
}
impl From<url::Url> for BlockVideo {
fn from(value: url::Url) -> Self {
Self {
url: Some(value),
..Self::default()
}
}
}
impl BlockType for BlockVideo {}
}
pub mod text_formatting {
use std::{ops::Range, str::FromStr};
use serde::{Deserialize, Serialize};
use super::objects;
#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum FormatValue {
Bold(FormatTypeBold),
Italic(FormatTypeItalic),
StrikeThrough(FormatTypeStrikeThrough),
Small(FormatTypeSmall),
Link(FormatTypeLink),
Mention(FormatTypeMention),
Color(FormatTypeColor),
}
// TODO: Make default() be private, removing the Default implementation
// since private implementations does't appear outside the module
trait FormatType: Default + From<Range<u64>> + From<String> {
fn new() -> Self {
Self::default()
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct FormatTypeLink {
r#type: String,
pub start: u64,
pub end: u64,
pub url: url::Url,
}
impl Default for FormatTypeLink {
fn default() -> Self {
Self {
r#type: String::from("link"),
start: 0,
end: 0,
url: url::Url::from_str("https://tumblr.com").unwrap(),
}
}
}
// TODO: Paywall type
#[derive(Debug, Deserialize, Serialize)]
pub struct FormatTypeMention {
r#type: String,
pub start: u64,
pub end: u64,
pub blog: objects::BlogInfo,
}
impl Default for FormatTypeMention {
fn default() -> Self {
Self {
r#type: String::from("mention"),
start: 0,
end: 0,
blog: objects::BlogInfo::default(),
}
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct FormatTypeColor {
r#type: String,
pub start: u64,
pub end: u64,
pub hex: String,
}
impl Default for FormatTypeColor {
fn default() -> Self {
Self {
r#type: String::from("link"),
start: 0,
end: 0,
hex: String::from("#ffffff"),
}
}
}
macro_rules! ImplInlines {
// If passed token is already defined (is a type), implements
// the FormattingInline traits
(for $($t:ty),+) => {
$(impl From<Range<u64>> for $t {
fn from(value: Range<u64>) -> Self {
Self {
start: value.start,
end: value.end,
..Self::default()
}
}
})*
$(impl From<String> for $t {
fn from(value: String) -> Self {
Self {
start: 0,
end: value.chars().count() as u64,
..Self::default()
}
}
})*
$(impl From<&str> for $t {
fn from(value: &str) -> Self {
Self {
start: 0,
end: value.chars().count() as u64,
..Self::default()
}
}
})*
$(impl FormatType for $t {})*
};
// Defines the struct and implements Default trait if the token is an
// identifier and a literal
(for $($t:ident $s:literal),+) => {
$(#[derive(Debug, Deserialize, Serialize)]
pub struct $t {
r#type: String,
pub start: u64,
pub end: u64,
})*
$(impl Default for $t {
fn default() -> Self {
Self {
r#type: String::from($s),
start: 0,
end: 0,
}
}
})*
$(ImplInlines!(for $t);)*
}
}
ImplInlines!(for
FormatTypeBold "bold",
FormatTypeItalic "italic",
FormatTypeStrikeThrough "strikethrough",
FormatTypeSmall "small"
);
ImplInlines!(for
FormatTypeLink,
FormatTypeMention,
FormatTypeColor
);
}
pub mod objects {
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Blog {
#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize)]
pub struct BlogInfo {
pub uuid: String,
pub name: String,
pub url: String,
pub name: Option<String>,
pub url: Option<url::Url>,
}
impl BlogInfo {
pub fn new(uuid: &str) -> Self {
Self::from(uuid)
}
}
impl Default for BlogInfo {
fn default() -> Self {
Self {
uuid: String::new(),
name: None,
url: None,
}
}
}
impl From<String> for BlogInfo {
fn from(value: String) -> Self {
Self {
uuid: value,
..Self::default()
}
}
}
impl From<&str> for BlogInfo {
fn from(value: &str) -> Self {
Self {
uuid: String::from(value),
..Self::default()
}
}
}
impl From<url::Url> for BlogInfo {
fn from(value: url::Url) -> Self {
Self {
url: Some(value),
..Self::default()
}
}
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Media {
pub r#type: Option<String>,
pub url: String,
pub width: Option<i32>,
pub height: Option<i32>,
pub poster: Option<MediaPoster>,
pub provider: Option<String>,
pub original_dimensions_missing: Option<bool>,
pub cropped: Option<bool>,
pub has_original_dimensions: Option<bool>,
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct IFrame {
pub url: String,
pub width: Option<i32>,
pub height: Option<i32>,
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct MediaPoster {
pub r#type: String,
pub url: String,
pub width: Option<String>,
pub height: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Attribution {
pub r#type: String,
pub url: String,
pub post: Post,
pub blug: Blog,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize)]
pub struct Post {
pub id: u64,
}
impl Post {
pub fn new(id: u64) -> Self {
Self::from(id)
}
}
impl Default for Post {
fn default() -> Self {
Self { id: 0 }
}
}
impl From<u64> for Post {
fn from(value: u64) -> Self {
Self { id: value }
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Media {
pub r#type: Option<String>,
pub url: url::Url,
pub width: Option<u64>,
pub height: Option<u64>,
pub original_dimensions_missing: Option<bool>,
pub cropped: Option<bool>,
pub has_original_dimentions: Option<bool>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct EmbedIframe {
pub url: url::Url,
pub width: u64,
pub height: u64,
}
}
pub mod attributions {
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use super::objects;
#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum AttributionValue {
Post(AttributionPost),
Link(AttributionLink),
Blog(AttributionBlog),
}
trait AttributionType: Default {
fn new() -> Self {
Self::default()
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct AttributionPost {
r#type: String,
pub url: url::Url,
pub post: objects::Post,
pub blog: objects::BlogInfo,
}
impl Default for AttributionPost {
fn default() -> Self {
Self {
r#type: String::from("post"),
url: url::Url::from_str("https://tumblr.com").unwrap(),
post: objects::Post::new(0),
blog: objects::BlogInfo::new("t:0"),
}
}
}
impl AttributionType for AttributionPost {}
#[derive(Debug, Deserialize, Serialize)]
pub struct AttributionLink {
r#type: String,
pub url: url::Url,
}
impl Default for AttributionLink {
fn default() -> Self {
Self {
r#type: String::from("link"),
url: url::Url::from_str("https://tumblr.com").unwrap(),
}
}
}
impl AttributionType for AttributionLink {}
#[derive(Debug, Deserialize, Serialize)]
pub struct AttributionBlog {
r#type: String,
pub url: Option<url::Url>,
pub blog: objects::BlogInfo,
}
impl Default for AttributionBlog {
fn default() -> Self {
Self {
r#type: String::from("blog"),
url: None,
blog: objects::BlogInfo::new("t:0"),
}
}
}
impl AttributionType for AttributionBlog {}
#[derive(Debug, Deserialize, Serialize)]
pub struct AttributionApp {
r#type: String,
pub url: url::Url,
pub app_name: Option<String>,
pub display_text: Option<String>,
pub logo: Option<objects::Media>,
}
impl Default for AttributionApp {
fn default() -> Self {
Self {
r#type: String::from("blog"),
url: url::Url::from_str("https://tumblr.com").unwrap(),
app_name: None,
display_text: None,
logo: None,
}
}
}
impl AttributionType for AttributionApp {}
}