diff --git a/src/frontmatter.rs b/src/frontmatter.rs new file mode 100644 index 0000000..52ec6c7 --- /dev/null +++ b/src/frontmatter.rs @@ -0,0 +1,82 @@ +use serde_yaml as yaml; +use std::collections::HashMap; + +#[derive(Debug)] +pub struct Frontmatter { + map: HashMap, +} + +impl Frontmatter +where + for<'a> T: serde::de::Deserialize<'a> + serde::Serialize, +{ + pub fn new(f: &mut String) -> Result, yaml::Error> { + let f = f.split("---").collect::>()[1]; + let m = match yaml::from_str::>(&f) { + Ok(m) => m, + Err(e) => return Err(e), + }; + Ok(Frontmatter { map: m }) + } + pub fn get(&self, key: &str) -> Option<&T> { + self.map.get(key) + } + pub fn set(&mut self, key: &str, value: T) { + self.map.insert(String::from(key), value); + } + pub fn to_string(&self) -> Result { + Ok(format!("---\n{}---\n\n", yaml::to_string(&self.map)?)) + } + pub fn to_map(&self) -> &HashMap { + &self.map + } +} + +pub fn to_yaml_value(value: Vec, json_to_string: bool) -> yaml::Value +where + T: ToString + std::fmt::Display, +{ + if value.len() >= 2 { + yaml::Value::Sequence( + value + .iter() + // This causes a recursion limit, which I'm not caring on fixing + // for now knowing the scope of this project as a hole. + // .map(|v| to_yaml_value(vec![v; 1], json_to_string)) + .map(|v| yaml::Value::String(v.to_string())) + .collect::>(), + ); + } + + let value = &value[0].to_string(); + + match value.to_lowercase().as_str() { + "null" | "~" => return yaml::Value::Null, + "true" | "yes" => return yaml::Value::Bool(true), + "false" | "no" => return yaml::Value::Bool(false), + _ => (), + } + + if let Ok(v) = value.parse::() { + return yaml::Value::Number(v.into()); + } + if let Ok(v) = value.parse::() { + return yaml::Value::Number(v.into()); + } + if let Ok(v) = value.parse::() { + return yaml::Value::Number(v.into()); + } + + match yaml::from_str::(value) { + Ok(v) => { + if json_to_string { + return yaml::Value::String(String::from(value)); + } else { + return v; + } + } + Err(_) => (), + } + + yaml::Value::String(String::from(value)) +} diff --git a/src/lib.rs b/src/lib.rs index e04f5bb..2fcb630 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,3 @@ +pub mod frontmatter; pub mod links; pub mod utils; diff --git a/src/main.rs b/src/main.rs index e30d0e2..39852de 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,13 @@ -use clap::{Parser, Subcommand}; +use core::panic; + +use clap::{ArgAction, Parser, Subcommand}; use clio::*; use comrak::nodes::NodeValue; -use mdparser::{links, utils}; +use mdparser::{ + frontmatter::{self, Frontmatter}, + links, utils, +}; #[derive(Parser, Debug)] #[command(version = "0.1", about = "", long_about = None, propagate_version = true)] @@ -38,9 +43,37 @@ enum Commands { #[arg(long)] not_remove_unfound: bool, }, + Frontmatter { + #[command(subcommand)] + command: FrontmatterCommands, + }, Not {}, } +#[derive(Debug, Subcommand)] +enum FrontmatterCommands { + Set { + #[clap()] + property: String, + + #[clap(num_args(1..))] + value: Vec, + + #[arg(short, long, action = ArgAction::SetTrue)] + json: bool, + }, + Get { + #[clap()] + property: String, + + #[arg(short, long, action = ArgAction::SetTrue)] + error_on_unfound: bool, + + #[arg(short, long, action = ArgAction::SetTrue)] + stderr_on_unfound: bool, + }, +} + fn main() { let mut cli = Cli::parse(); @@ -48,8 +81,6 @@ fn main() { let arena = comrak::Arena::new(); let ast = comrak::parse_document(&arena, &file, &mdparser::utils::default_options()); - // println!("{ast:#?}"); - match &cli.command { Commands::Links { path_root, @@ -81,6 +112,44 @@ fn main() { }; } }), + Commands::Frontmatter { command } => utils::iter_nodes(&ast, &|node| { + if let NodeValue::FrontMatter(ref mut f) = &mut node.data.borrow_mut().value { + let mut frontmatter: Frontmatter = match Frontmatter::new(f) { + Ok(f) => f, + Err(e) => panic!("{e:#?}"), + }; + match command { + FrontmatterCommands::Set { + property, + value, + json, + } => { + frontmatter.set( + &property, + frontmatter::to_yaml_value(value.to_vec(), !*json), + ); + } + FrontmatterCommands::Get { + property, + error_on_unfound, + stderr_on_unfound, + } => { + let v = frontmatter.get(property); + if let Some(v) = v { + print!("{v:#?}") + } else if *error_on_unfound { + panic!() + } else if *stderr_on_unfound { + eprint!("Not Found") + } + } + }; + *f = match frontmatter.to_string() { + Ok(s) => s, + Err(e) => panic!("{e:#?}"), + }; + } + }), _ => (), };