feat: prototype of the markdown to npf conversion

This commit is contained in:
Gustavo "Guz" L. de Mello
2024-04-24 15:15:02 -03:00
parent 468f8cf0dc
commit 03409dfd29
8 changed files with 198 additions and 30 deletions

View File

@@ -1,3 +1 @@
pub mod npf;
fn test() {}

View File

@@ -1,5 +1,143 @@
use std::{
borrow::{Borrow, BorrowMut},
cell::RefCell,
};
use comrak::{
arena_tree::Node,
nodes::{Ast, AstNode, NodeValue},
};
pub mod attributions;
pub mod content_blocks;
pub mod layout_blocks;
pub mod objects;
pub mod text_formatting;
use content_blocks::{BlockText, BlockValue};
use text_formatting::{FormatTypeBold, FormatTypeItalic, FormatValue};
#[derive(Debug)]
pub enum NPFConvertError {
TODO,
}
fn extract_text(contents: &Vec<BlockValue>) -> String {
contents
.iter()
.fold(String::new(), |mut a, c| {
if let BlockValue::Text(block) = c {
a.push_str(&format!(" {}", block.text));
}
a
})
.trim()
.to_string()
}
impl<'a> TryFrom<&'a Node<'a, RefCell<Ast>>> for objects::Post {
type Error = NPFConvertError;
fn try_from(value: &'a Node<'a, RefCell<Ast>>) -> Result<Self, Self::Error> {
let mut post = Self::new(0);
let nodes = value.children().into_iter();
let r: Result<Vec<_>, NPFConvertError> = nodes
.map(|n| match &n.data.borrow().value {
NodeValue::Paragraph => {
let mut paragraph_contents = Self::try_from(n)?.content;
post.content.append(&mut paragraph_contents);
Ok(())
}
NodeValue::Text(t) => {
let block_text = BlockText::from(String::from(t.clone()));
post.content.push(BlockValue::Text(block_text));
Ok(())
}
NodeValue::Strong => {
let mut content = Self::try_from(n)?.content;
let mut res = content.iter_mut().fold(
BlockText::new(&String::new()),
|mut acc, c| match c {
BlockValue::Text(t) => {
let text = &t.text.trim();
if let Some(ref mut f) = &mut t.formatting {
let offset = acc.text.chars().count() as u64;
f.iter_mut().for_each(|f| {
f.offset(offset);
});
if let Some(ref mut af) = acc.formatting {
af.append(f);
} else {
acc.formatting = Some(f.to_vec());
}
}
acc.text.push_str(&format!("{} ", text));
acc
}
_ => acc,
},
);
res.text = res.text.trim().to_string();
let format = FormatValue::Bold(FormatTypeBold::from(&res.text));
if let Some(ref mut f) = res.formatting {
f.push(format);
} else {
res.formatting = Some(vec![format]);
}
post.content.push(BlockValue::Text(res));
Ok(())
}
NodeValue::Emph => {
let mut content = Self::try_from(n)?.content;
let mut res = content.iter_mut().fold(
BlockText::new(&String::new()),
|mut acc, c| match c {
BlockValue::Text(t) => {
let text = &t.text.trim();
if let Some(ref mut f) = &mut t.formatting {
let offset = acc.text.chars().count() as u64;
f.iter_mut().for_each(|f| {
f.offset(offset);
});
if let Some(ref mut af) = acc.formatting {
af.append(f);
} else {
acc.formatting = Some(f.to_vec());
}
}
acc.text.push_str(&format!("{} ", text));
acc
}
_ => acc,
},
);
res.text = res.text.trim().to_string();
let format = FormatValue::Italic(FormatTypeItalic::from(&res.text));
if let Some(ref mut f) = res.formatting {
f.push(format);
} else {
res.formatting = Some(vec![format]);
}
// println!("italic {:#?}", res);
post.content.push(BlockValue::Text(res));
Ok(())
}
_ => Ok(()),
})
.collect();
if let Err(e) = r {
Err(e)
} else {
// println!("{:#?}", post);
Ok(post)
}
}
}
pub fn from<'a>(node: &'a Node<'a, RefCell<Ast>>) -> Result<objects::Post, NPFConvertError> {
objects::Post::try_from(node)
}
/*
Voluptate laboris sint cupidatat ullamco ut ea consectetur et est culpa et culpa duis
*/

View File

@@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use super::objects;
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(untagged)]
pub enum AttributionValue {
Post(AttributionPost),
@@ -12,7 +12,7 @@ pub enum AttributionValue {
Blog(AttributionBlog),
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct AttributionPost {
r#type: String,
pub url: url::Url,
@@ -41,7 +41,7 @@ impl AttributionPost {
}
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct AttributionLink {
r#type: String,
pub url: url::Url,
@@ -66,7 +66,7 @@ impl From<url::Url> for AttributionLink {
}
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct AttributionBlog {
r#type: String,
pub url: Option<url::Url>,
@@ -103,7 +103,7 @@ impl TryFrom<objects::BlogInfo> for AttributionBlog {
}
*/
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct AttributionApp {
r#type: String,
pub url: url::Url,

View File

@@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use super::{attributions, objects};
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(untagged)]
pub enum BlockValue {
Text(BlockText),
@@ -14,7 +14,7 @@ pub enum BlockValue {
Video(BlockVideo),
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "snake_case")]
pub enum BlockTextSubtype {
Heading1,
@@ -28,7 +28,7 @@ pub enum BlockTextSubtype {
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct BlockText {
r#type: String,
pub subtype: Option<BlockTextSubtype>,
@@ -68,7 +68,7 @@ impl From<&str> for BlockText {
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct BlockImage {
r#type: String,
pub media: Vec<objects::Media>,
@@ -111,7 +111,7 @@ impl From<objects::Media> for BlockImage {
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct BlockLink {
r#type: String,
pub url: url::Url,
@@ -149,7 +149,7 @@ impl From<url::Url> for BlockLink {
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct BlockAudio {
r#type: String,
pub media: Option<objects::Media>,
@@ -210,7 +210,7 @@ impl From<url::Url> for BlockAudio {
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct BlockVideo {
r#type: String,
pub url: Option<url::Url>,

View File

@@ -1,13 +1,13 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(untagged)]
pub enum BlockValue {
Rows(BlockRows),
Ask(BlockAsk),
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct BlockRows {
r#type: String,
pub display: Vec<DisplayBlocks>,
@@ -42,7 +42,7 @@ impl From<DisplayBlocks> for BlockRows {
}
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct DisplayBlocks {
pub blocks: Vec<u64>,
pub mode: Option<String>,
@@ -75,7 +75,7 @@ impl From<u64> for DisplayBlocks {
}
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct BlockAsk {
r#type: String,
blocks: Vec<u64>,

View File

@@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct BlogInfo {
pub uuid: String,
pub name: Option<String>,
@@ -48,14 +48,14 @@ impl From<url::Url> for BlogInfo {
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ReblogTrailPost {
pub id: String,
pub timestamp: Option<String>,
pub is_commercial: Option<bool>,
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ReblogTrail {
pub post: Option<ReblogTrailPost>,
pub blog: Option<BlogInfo>,
@@ -65,7 +65,7 @@ pub struct ReblogTrail {
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Avatar {
pub width: u64,
pub height: u64,
@@ -74,7 +74,7 @@ pub struct Avatar {
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Post {
object_type: String,
pub id: u64,
@@ -188,7 +188,7 @@ impl From<u64> for Post {
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Media {
pub r#type: Option<String>,
pub url: url::Url,
@@ -223,7 +223,7 @@ impl From<url::Url> for Media {
}
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct EmbedIframe {
pub url: url::Url,
pub width: u64,

View File

@@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use super::objects;
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(untagged)]
pub enum FormatValue {
Bold(FormatTypeBold),
@@ -15,12 +15,25 @@ pub enum FormatValue {
Mention(FormatTypeMention),
Color(FormatTypeColor),
}
impl FormatValue {
pub fn offset(&mut self, offset: u64) {
match self {
FormatValue::Bold(ref mut f) => f.offset(offset),
FormatValue::Italic(ref mut f) => f.offset(offset),
FormatValue::Link(ref mut f) => f.offset(offset),
FormatValue::Small(ref mut f) => f.offset(offset),
FormatValue::Color(ref mut f) => f.offset(offset),
FormatValue::Mention(ref mut f) => f.offset(offset),
FormatValue::StrikeThrough(ref mut f) => f.offset(offset),
}
}
}
trait FormatType: From<Range<u64>> + From<String> {
fn default() -> Self;
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct FormatTypeLink {
r#type: String,
pub start: u64,
@@ -46,7 +59,7 @@ impl FormatType for FormatTypeLink {
}
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct FormatTypeMention {
r#type: String,
pub start: u64,
@@ -72,7 +85,7 @@ impl FormatType for FormatTypeMention {
}
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct FormatTypeColor {
r#type: String,
pub start: u64,
@@ -120,6 +133,15 @@ macro_rules! ImplInlines {
}
}
})*
$(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 {
@@ -129,11 +151,17 @@ macro_rules! ImplInlines {
}
}
})*
$(impl $t {
pub fn offset(&mut self, offset: u64) {
self.start += offset;
self.end += offset;
}
})*
};
// 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)]
$(#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct $t {
r#type: String,
pub start: u64,

View File

@@ -4,6 +4,7 @@ use std::path::PathBuf;
use clap::{ArgAction, Parser, Subcommand};
use clio::Input;
use mdparser::convert;
use mdparser::frontmatter::Frontmatter;
use mdparser::links;
use mdparser::utils;
@@ -72,6 +73,9 @@ fn main() {
let arena = comrak::Arena::new();
let ast = comrak::parse_document(&arena, &file, &mdparser::utils::default_options());
let _ = convert::npf::from(ast);
// println!("{:#?}", ast);
let result = match cli.command {
Commands::Links { list, replace_url } => {
let list = if replace_url.len() == 0 && !list {