refactor!: rewrote all npf types and remove old npf code
This commit is contained in:
73
Cargo.lock
generated
73
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
188
src/convert.rs
188
src/convert.rs
@@ -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() {}
|
||||
|
||||
@@ -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 {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user