diff --git a/Cargo.lock b/Cargo.lock index 3c7fb16..34f5334 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -382,7 +382,7 @@ dependencies = [ [[package]] name = "snekdown" -version = "0.8.0" +version = "0.8.1" dependencies = [ "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index c7d506f..b6d8bf5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "snekdown" -version = "0.8.0" +version = "0.8.1" authors = ["trivernis "] edition = "2018" license-file = "LICENSE" diff --git a/README.md b/README.md index f72e3e5..75c8a49 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,7 @@ Set the size of an image !(url)[width = 42% height=auto] Set the source of a quote -[author=Me date=[[date]] display="author - date"]> It's me +[author=Me date=[[date]] display="{{author}} - {{date}}"]> It's me Set options for placeholders [[toc]][ordered] diff --git a/src/format/html.rs b/src/format/html.rs index 37bfcaf..209dce2 100644 --- a/src/format/html.rs +++ b/src/format/html.rs @@ -1,3 +1,4 @@ +use crate::format::Template; use crate::parsing::elements::*; use htmlescape::{encode_attribute, encode_minimal}; use minify::html::minify; @@ -404,12 +405,12 @@ impl ToHtml for Anchor { impl ToHtml for InlineMetadata { fn to_html(&self) -> String { if let Some(MetadataValue::String(format)) = self.data.get("display") { - let mut format = format.clone(); + let mut template = Template::new(format.clone()); self.data .iter() - .for_each(|(k, v)| format = format.replace(k, v.to_html().as_str())); + .for_each(|(k, v)| template.add_replacement(k, v.to_html().as_str())); - format + template.render() } else { self.data.iter().fold("".to_string(), |s, (k, v)| { format!("{} {}={},", s, k, v.to_html()) diff --git a/src/format/mod.rs b/src/format/mod.rs index 3d4613d..3541019 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -1 +1,42 @@ +use regex::Regex; +use std::collections::HashMap; + pub mod html; + +pub struct Template { + value: String, + replacements: HashMap, +} + +impl Template { + pub fn new(value: String) -> Self { + Self { + value, + replacements: HashMap::new(), + } + } + + pub fn add_replacement(&mut self, name: &str, val: &str) { + self.replacements.insert(name.to_string(), val.to_string()); + } + + pub fn set_replacements(&mut self, replacements: HashMap) { + self.replacements = replacements; + } + + pub fn render(&mut self) -> String { + lazy_static::lazy_static! { static ref RE_REP: Regex = Regex::new(r"\{\{([^}]*)}}").unwrap(); } + let mut ret_string = self.value.clone(); + RE_REP.find_iter(&self.value).for_each(|m| { + let full_match = m.as_str(); + let name = &full_match[2..full_match.len() - 2]; + if let Some(val) = self.replacements.get(name) { + ret_string = ret_string.replace(full_match, val) + } else { + ret_string = ret_string.replace(full_match, "") + } + }); + + ret_string + } +} diff --git a/src/parsing/placeholders.rs b/src/parsing/placeholders.rs index 1fb900b..9e5003e 100644 --- a/src/parsing/placeholders.rs +++ b/src/parsing/placeholders.rs @@ -1,4 +1,5 @@ use super::elements::*; +use crate::format::Template; use chrono::prelude::*; use regex::Regex; use std::sync::{Arc, Mutex, MutexGuard}; @@ -53,27 +54,28 @@ impl BibEntry { if let Some(display) = &self.display { let value = display.lock().unwrap(); if let MetadataValue::String(format) = &value.value { - let mut format = format.clone(); + let mut template = Template::new(format.clone()); + if let Some(author) = &self.author { - format = format.replace("author", author); + template.add_replacement(B_AUTHOR, author.as_str()); } if let Some(date) = &self.date { - format = format.replace("date", date); + template.add_replacement(B_DATE, date.as_str()); } if let Some(url) = &self.url { - format = format.replace("url", url) + template.add_replacement(B_URL, url.as_str()); } if let Some(title) = &self.title { - format = format.replace("title", title); + template.add_replacement(B_TITLE, title.as_str()); } if let Some(publisher) = &self.publisher { - format = format.replace("publisher", publisher); + template.add_replacement(B_PUBLISHER, publisher.as_str()); } if let Some(notes) = &self.notes { - format = format.replace("notes", notes); + template.add_replacement(B_NOTES, notes.as_str()); } - format + template.render() } else { format!("'Invalid formatter!' {:?}", self) }