diff --git a/Cargo.lock b/Cargo.lock index f482965..fb36dd5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -566,7 +566,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "snekdown" -version = "0.15.0" +version = "0.15.1" dependencies = [ "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "colored 1.9.3 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index ab2fad5..900e5eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "snekdown" -version = "0.15.0" +version = "0.15.1" authors = ["trivernis "] edition = "2018" license-file = "LICENSE" diff --git a/src/format/html.rs b/src/format/html.rs index 8cac8c6..5380d3e 100644 --- a/src/format/html.rs +++ b/src/format/html.rs @@ -2,6 +2,7 @@ use crate::format::PlaceholderTemplate; use crate::parsing::bibliography::{BibEntry, BibReference}; use crate::parsing::configuration::Value; use crate::parsing::elements::*; +use crate::parsing::templates::{Template, TemplateVariable}; use htmlescape::{encode_attribute, encode_minimal}; use minify::html::minify; use std::cell::RefCell; @@ -40,7 +41,7 @@ impl ToHtml for Line { Line::Ruler(ruler) => ruler.to_html(), Line::Anchor(anchor) => anchor.to_html(), Line::Centered(centered) => centered.to_html(), - Line::BibEntry(bib) => bib.lock().unwrap().to_html(), + Line::BibEntry(bib) => bib.read().unwrap().to_html(), } } } @@ -56,13 +57,13 @@ impl ToHtml for Inline { Inline::Underlined(under) => under.to_html(), Inline::Bold(bold) => bold.to_html(), Inline::Image(img) => img.to_html(), - Inline::Placeholder(placeholder) => placeholder.lock().unwrap().to_html(), + Inline::Placeholder(placeholder) => placeholder.read().unwrap().to_html(), Inline::Superscript(superscript) => superscript.to_html(), Inline::Checkbox(checkbox) => checkbox.to_html(), Inline::Emoji(emoji) => emoji.to_html(), Inline::Colored(colored) => colored.to_html(), - Inline::BibReference(bibref) => bibref.lock().unwrap().to_html(), - Inline::TemplateVar(var) => var.lock().unwrap().to_html(), + Inline::BibReference(bibref) => bibref.read().unwrap().to_html(), + Inline::TemplateVar(var) => var.read().unwrap().to_html(), } } } @@ -77,7 +78,7 @@ impl ToHtml for Block { Block::Quote(quote) => quote.to_html(), Block::Section(section) => section.to_html(), Block::Import(import) => import.to_html(), - Block::Placeholder(placeholder) => placeholder.lock().unwrap().to_html(), + Block::Placeholder(placeholder) => placeholder.read().unwrap().to_html(), } } } @@ -87,7 +88,7 @@ impl ToHtml for MetadataValue { match self { MetadataValue::String(string) => encode_minimal(string), MetadataValue::Integer(num) => format!("{}", num), - MetadataValue::Placeholder(ph) => ph.lock().unwrap().to_html(), + MetadataValue::Placeholder(ph) => ph.read().unwrap().to_html(), MetadataValue::Bool(b) => format!("{}", b), MetadataValue::Float(f) => format!("{}", f), MetadataValue::Template(t) => t.to_html(), @@ -123,7 +124,7 @@ impl ToHtml for Document { impl ToHtml for Import { fn to_html(&self) -> String { - let anchor = self.anchor.lock().unwrap(); + let anchor = self.anchor.read().unwrap(); if let Some(document) = &anchor.document { document.to_html() } else { @@ -485,7 +486,7 @@ impl ToHtml for BibEntry { return "".to_string(); } if let Some(display) = &self.display { - let display = display.lock().unwrap(); + let display = display.read().unwrap(); if let Value::Template(template) = display.get() { let replacements = self .as_map() diff --git a/src/parsing/bibliography.rs b/src/parsing/bibliography.rs index ac03fe8..80fce30 100644 --- a/src/parsing/bibliography.rs +++ b/src/parsing/bibliography.rs @@ -3,7 +3,7 @@ use crate::parsing::configuration::keys::{BIB_DISPLAY, BIB_HIDE_UNUSED}; use crate::parsing::configuration::{ConfigRefEntry, Configuration, Value}; use crate::parsing::elements::Metadata; use std::collections::HashMap; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, RwLock}; const B_NUMBER: &str = "number"; const B_AUTHOR: &str = "author"; @@ -31,14 +31,14 @@ pub struct BibEntry { #[derive(Clone, Debug)] pub struct BibReference { pub(crate) key: String, - pub(crate) reference_entry: Option>>, + pub(crate) reference_entry: Option>>, pub(crate) display: Option, } #[derive(Clone, Debug)] pub struct Bibliography { - entries: HashMap>>, - references: Vec>>, + entries: HashMap>>, + references: Vec>>, } impl BibEntry { @@ -110,7 +110,7 @@ impl BibEntry { pub fn is_visible(&self) -> bool { if let Some(hide_cfg) = &self.hide_unused { - let hide_cfg = hide_cfg.lock().unwrap(); + let hide_cfg = hide_cfg.read().unwrap(); if let Value::Bool(b) = hide_cfg.get() { if *b && self.ref_count == 0 { return false; @@ -132,15 +132,15 @@ impl BibReference { } /// sets the reference to the bib entry - pub(crate) fn set_entry(&mut self, entry: Arc>) { + pub(crate) fn set_entry(&mut self, entry: Arc>) { self.reference_entry = Some(entry) } pub(crate) fn get_formatted(&self) -> String { if let Some(entry) = &self.reference_entry { - let entry = entry.lock().unwrap(); + let entry = entry.read().unwrap(); if let Some(display) = &self.display { - let display = display.lock().unwrap(); + let display = display.read().unwrap(); let mut template = PlaceholderTemplate::new(display.get().as_string()); template.set_replacements(entry.as_map()); return template.render(); @@ -163,10 +163,10 @@ impl Bibliography { pub(crate) fn assign_entry_data(&mut self) { let mut count = 0; self.references.iter().for_each(|e| { - let mut reference = e.lock().unwrap(); + let mut reference = e.write().unwrap(); if let Some(entry) = self.entries.get(&reference.key) { { - let mut entry_raw = entry.lock().unwrap(); + let mut entry_raw = entry.write().unwrap(); let ref_count = entry_raw.ref_count; entry_raw.set_ref_count(ref_count + 1); } @@ -174,7 +174,7 @@ impl Bibliography { } }); self.entries.iter().for_each(|(_, e)| { - let mut entry = e.lock().unwrap(); + let mut entry = e.write().unwrap(); if entry.is_visible() { count += 1; entry.set_number(count) @@ -182,12 +182,12 @@ impl Bibliography { }); } - pub fn add_ref_entry(&mut self, entry: Arc>) { + pub fn add_ref_entry(&mut self, entry: Arc>) { self.references.push(entry) } - pub fn add_bib_entry(&mut self, entry: Arc>) { - let key = entry.lock().unwrap().key.clone(); + pub fn add_bib_entry(&mut self, entry: Arc>) { + let key = entry.read().unwrap().key.clone(); self.entries.insert(key, entry); } diff --git a/src/parsing/configuration/mod.rs b/src/parsing/configuration/mod.rs index 6b406df..a009cf3 100644 --- a/src/parsing/configuration/mod.rs +++ b/src/parsing/configuration/mod.rs @@ -2,9 +2,10 @@ use crate::parsing::configuration::config::RootConfig; use crate::parsing::configuration::keys::{ BIB_DISPLAY, BIB_HIDE_UNUSED, BIB_REF_DISPLAY, META_AUTHOR, META_DATE, META_TITLE, }; -use crate::parsing::elements::{MetadataValue, Template}; +use crate::parsing::elements::MetadataValue; +use crate::parsing::templates::Template; use std::collections::HashMap; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, RwLock}; pub mod config; pub(crate) mod keys; @@ -23,11 +24,11 @@ pub struct ConfigEntry { inner: Value, } -pub type ConfigRefEntry = Arc>; +pub type ConfigRefEntry = Arc>; #[derive(Clone, Debug)] pub struct Configuration { - config: Arc>>, + config: Arc>>, } impl Value { @@ -59,7 +60,7 @@ impl ConfigEntry { impl Configuration { pub fn new() -> Self { Self { - config: Arc::new(Mutex::new(HashMap::new())), + config: Arc::new(RwLock::new(HashMap::new())), } } @@ -98,9 +99,9 @@ impl Configuration { /// returns the value of a config entry pub fn get_entry(&self, key: &str) -> Option { - let config = self.config.lock().unwrap(); + let config = self.config.read().unwrap(); if let Some(entry) = config.get(key) { - let value = entry.lock().unwrap(); + let value = entry.read().unwrap(); Some(value.clone()) } else { None @@ -109,7 +110,7 @@ impl Configuration { /// returns a config entry that is a reference to a value pub fn get_ref_entry(&self, key: &str) -> Option { - let config = self.config.lock().unwrap(); + let config = self.config.read().unwrap(); if let Some(entry) = config.get(&key.to_string()) { Some(Arc::clone(entry)) } else { @@ -119,13 +120,13 @@ impl Configuration { /// Sets a config parameter pub fn set(&mut self, key: &str, value: Value) { - let mut config = self.config.lock().unwrap(); + let mut config = self.config.write().unwrap(); if let Some(entry) = config.get(&key.to_string()) { - entry.lock().unwrap().set(value) + entry.write().unwrap().set(value) } else { config.insert( key.to_string(), - Arc::new(Mutex::new(ConfigEntry::new(value))), + Arc::new(RwLock::new(ConfigEntry::new(value))), ); } } diff --git a/src/parsing/elements.rs b/src/parsing/elements.rs index 4195637..28ba5a6 100644 --- a/src/parsing/elements.rs +++ b/src/parsing/elements.rs @@ -1,9 +1,10 @@ use crate::parsing::bibliography::{BibEntry, BibReference, Bibliography}; use crate::parsing::configuration::Configuration; use crate::parsing::placeholders::ProcessPlaceholders; +use crate::parsing::templates::{Template, TemplateVariable}; use std::collections::HashMap; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, RwLock}; pub const SECTION: &str = "section"; pub const PARAGRAPH: &str = "paragraph"; @@ -19,7 +20,7 @@ pub enum MetadataValue { Integer(i64), Float(f64), Bool(bool), - Placeholder(Arc>), + Placeholder(Arc>), Template(Template), } @@ -39,7 +40,7 @@ pub enum Block { CodeBlock(CodeBlock), Quote(Quote), Import(Import), - Placeholder(Arc>), + Placeholder(Arc>), } #[derive(Clone, Debug)] @@ -48,7 +49,7 @@ pub enum Line { Ruler(Ruler), Anchor(Anchor), Centered(Centered), - BibEntry(Arc>), + BibEntry(Arc>), } #[derive(Clone, Debug)] @@ -56,7 +57,7 @@ pub struct Document { pub elements: Vec, pub(crate) is_root: bool, pub(crate) path: Option, - pub(crate) placeholders: Vec>>, + pub(crate) placeholders: Vec>>, pub config: Configuration, pub bibliography: Bibliography, } @@ -125,7 +126,7 @@ pub struct Quote { #[derive(Clone, Debug)] pub struct Import { pub(crate) path: String, - pub(crate) anchor: Arc>, + pub(crate) anchor: Arc>, } #[derive(Clone, Debug)] @@ -157,12 +158,12 @@ pub enum Inline { Superscript(SuperscriptText), Url(Url), Image(Image), - Placeholder(Arc>), + Placeholder(Arc>), Checkbox(Checkbox), Emoji(Emoji), Colored(Colored), - BibReference(Arc>), - TemplateVar(Arc>), + BibReference(Arc>), + TemplateVar(Arc>), } #[derive(Clone, Debug)] @@ -247,20 +248,6 @@ pub struct Colored { pub(crate) color: String, } -#[derive(Clone, Debug)] -pub struct Template { - pub(crate) text: Vec, - pub(crate) variables: HashMap>>, -} - -#[derive(Clone, Debug)] -pub struct TemplateVariable { - pub(crate) prefix: String, - pub(crate) name: String, - pub(crate) suffix: String, - pub(crate) value: Option, -} - // implementations impl Document { @@ -279,7 +266,7 @@ impl Document { self.elements.push(element) } - pub fn add_placeholder(&mut self, placeholder: Arc>) { + pub fn add_placeholder(&mut self, placeholder: Arc>) { self.placeholders.push(placeholder); } @@ -295,7 +282,7 @@ impl Document { } } Block::Import(imp) => { - let anchor = imp.anchor.lock().unwrap(); + let anchor = imp.anchor.read().unwrap(); if let Some(doc) = &anchor.document { list.items.append(&mut doc.create_toc(ordered).items) } @@ -334,7 +321,7 @@ impl Document { } Block::Import(imp) => { let arc_anchor = Arc::clone(&imp.anchor); - let anchor = &mut arc_anchor.lock().unwrap(); + let anchor = &mut arc_anchor.write().unwrap(); if let Some(doc) = &mut anchor.document { self.placeholders.append(&mut doc.placeholders); self.bibliography.combine(&mut doc.bibliography); @@ -415,11 +402,11 @@ impl Section { if section.header.size == self.header.size + 1 { self.elements.push(Block::Section(section)) } else { - let has_parent = Mutex::new(AtomicBool::new(true)); + let has_parent = RwLock::new(AtomicBool::new(true)); let iterator = self.elements.iter_mut().rev().filter(|e| { if let Block::Section(sec) = e { if sec.header.size > section.header.size { - has_parent.lock().unwrap().store(true, Ordering::Relaxed); + has_parent.write().unwrap().store(true, Ordering::Relaxed); true } else { false @@ -429,7 +416,7 @@ impl Section { } }); - if has_parent.lock().unwrap().load(Ordering::Relaxed) { + if has_parent.read().unwrap().load(Ordering::Relaxed) { for sec in iterator { if let Block::Section(sec) = sec { if sec.header.size < section.header.size { @@ -608,208 +595,3 @@ impl Metadata for InlineMetadata { } } } - -impl Element { - pub fn get_template_variables(&self) -> Vec>> { - match self { - Element::Block(block) => block - .get_template_variables() - .iter() - .filter_map(|e| e.clone()) - .collect(), - Element::Inline(inline) => vec![inline.get_template_variable()] - .iter() - .filter_map(|e| e.clone()) - .collect(), - Element::Line(line) => line - .get_template_variables() - .iter() - .filter_map(|e| e.clone()) - .collect(), - } - } - - pub fn freeze_variables(&mut self) -> Option>> { - match self { - Element::Block(b) => b.freeze_template_variables(), - Element::Line(l) => l.freeze_variables(), - Element::Inline(i) => return i.freeze_variables(), - } - - None - } -} - -impl Block { - pub fn get_template_variables(&self) -> Vec>>> { - match self { - Block::Section(sec) => sec - .elements - .iter() - .map(|e| e.get_template_variables()) - .flatten() - .collect(), - Block::Paragraph(par) => par - .elements - .iter() - .map(|l| l.get_template_variables()) - .flatten() - .collect(), - Block::Quote(q) => q - .text - .iter() - .map(|t| t.subtext.iter().map(|i| i.get_template_variable())) - .flatten() - .collect(), - _ => Vec::new(), - } - } - - pub fn freeze_template_variables(&mut self) { - match self { - Block::Section(s) => s - .elements - .iter_mut() - .for_each(|b| b.freeze_template_variables()), - Block::Paragraph(p) => p.elements.iter_mut().for_each(|l| l.freeze_variables()), - Block::Quote(q) => q.text.iter_mut().for_each(|t| { - t.subtext = t - .subtext - .iter_mut() - .map(|i| { - if let Some(t) = i.freeze_variables() { - Inline::TemplateVar(t) - } else { - (*i).clone() - } - }) - .collect() - }), - _ => {} - } - } -} - -impl Line { - pub fn get_template_variables(&self) -> Vec>>> { - match self { - Line::Text(line) => line - .subtext - .iter() - .map(|s| s.get_template_variable()) - .collect(), - _ => Vec::new(), - } - } - - pub fn freeze_variables(&mut self) { - match self { - Line::Text(text) => { - text.subtext = text - .subtext - .iter_mut() - .map(|i| { - if let Some(t) = i.freeze_variables() { - Inline::TemplateVar(t) - } else { - (*i).clone() - } - }) - .collect() - } - _ => {} - } - } -} - -impl Inline { - pub fn get_template_variable(&self) -> Option>> { - match self { - Inline::TemplateVar(temp) => Some(Arc::clone(temp)), - Inline::Colored(col) => col.value.get_template_variable(), - Inline::Superscript(sup) => sup.value.get_template_variable(), - Inline::Striked(striked) => striked.value.get_template_variable(), - Inline::Underlined(under) => under.value.get_template_variable(), - Inline::Italic(it) => it.value.get_template_variable(), - Inline::Bold(bo) => bo.value.get_template_variable(), - _ => None, - } - } - - pub fn freeze_variables(&mut self) -> Option>> { - match self { - Inline::TemplateVar(temp) => { - let temp = temp.lock().unwrap(); - return Some(Arc::new(Mutex::new((*temp).clone()))); - } - Inline::Colored(col) => { - if let Some(temp) = col.value.freeze_variables() { - col.value = Box::new(Inline::TemplateVar(temp)) - } - } - Inline::Superscript(sup) => { - if let Some(temp) = sup.value.freeze_variables() { - sup.value = Box::new(Inline::TemplateVar(temp)) - } - } - Inline::Striked(striked) => { - if let Some(temp) = striked.value.freeze_variables() { - striked.value = Box::new(Inline::TemplateVar(temp)) - } - } - Inline::Underlined(under) => { - if let Some(temp) = under.value.freeze_variables() { - under.value = Box::new(Inline::TemplateVar(temp)) - } - } - Inline::Italic(it) => { - if let Some(temp) = it.value.freeze_variables() { - it.value = Box::new(Inline::TemplateVar(temp)) - } - } - Inline::Bold(bo) => { - if let Some(temp) = bo.value.freeze_variables() { - bo.value = Box::new(Inline::TemplateVar(temp)) - } - } - _ => {} - } - None - } -} - -impl Template { - pub fn render(&self, replacements: HashMap) -> Vec { - replacements.iter().for_each(|(k, r)| { - if let Some(v) = self.variables.get(k) { - v.lock().unwrap().set_value(r.clone()) - } - }); - let elements = self - .text - .iter() - .map(|e| { - let mut e = e.clone(); - if let Some(template) = e.freeze_variables() { - Element::Inline(Box::new(Inline::TemplateVar(template))) - } else { - e - } - }) - .collect(); - self.variables - .iter() - .for_each(|(_, v)| v.lock().unwrap().reset()); - - elements - } -} - -impl TemplateVariable { - pub fn set_value(&mut self, value: Element) { - self.value = Some(value) - } - pub fn reset(&mut self) { - self.value = None - } -} diff --git a/src/parsing/inline.rs b/src/parsing/inline.rs index b575536..ca07d1e 100644 --- a/src/parsing/inline.rs +++ b/src/parsing/inline.rs @@ -3,9 +3,10 @@ use super::elements::*; use super::tokens::*; use crate::parsing::bibliography::BibReference; use crate::parsing::configuration::keys::BIB_REF_DISPLAY; +use crate::parsing::templates::TemplateVariable; use crate::parsing::utils::{ParseError, ParseResult}; use crate::Parser; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, RwLock}; pub(crate) trait ParseInline { fn parse_surrounded(&mut self, surrounding: &char) -> ParseResult; @@ -21,8 +22,8 @@ pub(crate) trait ParseInline { fn parse_superscript(&mut self) -> ParseResult; fn parse_emoji(&mut self) -> ParseResult; fn parse_colored(&mut self) -> ParseResult; - fn parse_bibref(&mut self) -> ParseResult>>; - fn parse_template_variable(&mut self) -> ParseResult>>; + fn parse_bibref(&mut self) -> ParseResult>>; + fn parse_template_variable(&mut self) -> ParseResult>>; fn parse_plain(&mut self) -> ParseResult; } @@ -238,13 +239,13 @@ impl ParseInline for Parser { }) } - fn parse_bibref(&mut self) -> ParseResult<Arc<Mutex<BibReference>>> { + fn parse_bibref(&mut self) -> ParseResult<Arc<RwLock<BibReference>>> { let start_index = self.index; self.assert_special_sequence(&SQ_BIBREF_START, start_index)?; self.skip_char(); let key = self.get_string_until_or_revert(&[BIBREF_CLOSE], &[SPACE, LB], start_index)?; self.skip_char(); - let ref_entry = Arc::new(Mutex::new(BibReference::new( + let ref_entry = Arc::new(RwLock::new(BibReference::new( key, self.document.config.get_ref_entry(BIB_REF_DISPLAY), ))); @@ -256,7 +257,7 @@ impl ParseInline for Parser { } /// parses a template variable {prefix{name}suffix} - fn parse_template_variable(&mut self) -> ParseResult<Arc<Mutex<TemplateVariable>>> { + fn parse_template_variable(&mut self) -> ParseResult<Arc<RwLock<TemplateVariable>>> { let start_index = self.index; self.assert_special(&TEMP_VAR_OPEN, start_index)?; self.skip_char(); @@ -266,7 +267,7 @@ impl ParseInline for Parser { self.skip_char(); let suffix = self.get_string_until_or_revert(&[TEMP_VAR_CLOSE], &[LB], start_index)?; self.skip_char(); - Ok(Arc::new(Mutex::new(TemplateVariable { + Ok(Arc::new(RwLock::new(TemplateVariable { value: None, name, prefix, diff --git a/src/parsing/mod.rs b/src/parsing/mod.rs index cba2112..7de0b32 100644 --- a/src/parsing/mod.rs +++ b/src/parsing/mod.rs @@ -5,6 +5,7 @@ pub mod elements; pub mod inline; pub mod parser; pub mod placeholders; +pub mod templates; pub mod tokens; #[macro_use] diff --git a/src/parsing/parser.rs b/src/parsing/parser.rs index eccc859..ab2ee71 100644 --- a/src/parsing/parser.rs +++ b/src/parsing/parser.rs @@ -2,7 +2,9 @@ use super::elements::*; use super::tokens::*; use crate::parsing::bibliography::BibEntry; use crate::parsing::charstate::CharStateMachine; +use crate::parsing::configuration::Configuration; use crate::parsing::inline::ParseInline; +use crate::parsing::templates::{GetTemplateVariables, Template, TemplateVariable}; use crate::parsing::utils::{ParseError, ParseResult}; use colored::*; use crossbeam_utils::sync::WaitGroup; @@ -11,7 +13,7 @@ use std::fs::File; use std::io; use std::io::{BufRead, BufReader, Cursor}; use std::path::Path; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; use std::thread; pub struct Parser { @@ -107,6 +109,7 @@ impl Parser { if let Some(ch) = text.get(0) { current_char = *ch } + let document = Document::new(!is_child); Self { index: 0, text, @@ -121,12 +124,16 @@ impl Parser { previous_char: ' ', inline_break_at: Vec::new(), block_break_at: Vec::new(), - document: Document::new(!is_child), + document, reader, parse_variables: false, } } + pub fn set_config(&mut self, config: Configuration) { + self.document.config = config; + } + /// Returns the text of the parser as a string fn get_text(&self) -> String { self.text @@ -159,7 +166,7 @@ impl Parser { } /// starts up a new thread to parse the imported document - fn import_document(&mut self, path: String) -> ParseResult<Arc<Mutex<ImportAnchor>>> { + fn import_document(&mut self, path: String) -> ParseResult<Arc<RwLock<ImportAnchor>>> { let path = self.transform_path(path); let path_info = Path::new(&path); if !path_info.exists() || !path_info.is_file() { @@ -183,15 +190,17 @@ impl Parser { } paths.push(path.clone()); } - let anchor = Arc::new(Mutex::new(ImportAnchor::new())); + let anchor = Arc::new(RwLock::new(ImportAnchor::new())); let anchor_clone = Arc::clone(&anchor); let wg = self.wg.clone(); let paths = Arc::clone(&self.paths); + let config = self.document.config.clone(); let _ = thread::spawn(move || { let mut parser = Parser::child_from_file(path, paths).unwrap(); + parser.set_config(config); let document = parser.parse(); - anchor_clone.lock().unwrap().set_document(document); + anchor_clone.write().unwrap().set_document(document); drop(wg); }); @@ -707,7 +716,7 @@ impl Parser { } } - fn parse_bib_entry(&mut self) -> ParseResult<Arc<Mutex<BibEntry>>> { + fn parse_bib_entry(&mut self) -> ParseResult<Arc<RwLock<BibEntry>>> { let start_index = self.index; self.seek_inline_whitespace(); self.assert_special(&BIB_KEY_OPEN, start_index)?; @@ -723,7 +732,7 @@ impl Parser { let url = self.get_string_until_or_revert(&[LB], &[], start_index)?; BibEntry::from_url(key, url, &self.document.config) }; - let entry_ref = Arc::new(Mutex::new(entry)); + let entry_ref = Arc::new(RwLock::new(entry)); self.document .bibliography .add_bib_entry(Arc::clone(&entry_ref)); @@ -742,7 +751,7 @@ impl Parser { } /// parses a placeholder element - pub(crate) fn parse_placeholder(&mut self) -> Result<Arc<Mutex<Placeholder>>, ParseError> { + pub(crate) fn parse_placeholder(&mut self) -> Result<Arc<RwLock<Placeholder>>, ParseError> { let start_index = self.index; self.assert_special_sequence(&SQ_PHOLDER_START, self.index)?; self.skip_char(); @@ -760,7 +769,7 @@ impl Parser { None }; - let placeholder = Arc::new(Mutex::new(Placeholder::new(name, metadata))); + let placeholder = Arc::new(RwLock::new(Placeholder::new(name, metadata))); self.document.add_placeholder(Arc::clone(&placeholder)); Ok(placeholder) @@ -819,14 +828,14 @@ impl Parser { self.inline_break_at.clear(); self.assert_special(&TEMPLATE, start_index)?; self.skip_char(); - let vars: HashMap<String, Arc<Mutex<TemplateVariable>>> = elements + let vars: HashMap<String, Arc<RwLock<TemplateVariable>>> = elements .iter() .map(|e| e.get_template_variables()) .flatten() - .map(|e: Arc<Mutex<TemplateVariable>>| { + .map(|e: Arc<RwLock<TemplateVariable>>| { let name; { - name = e.lock().unwrap().name.clone(); + name = e.read().unwrap().name.clone(); }; (name, e) diff --git a/src/parsing/placeholders.rs b/src/parsing/placeholders.rs index 259546e..4e02e69 100644 --- a/src/parsing/placeholders.rs +++ b/src/parsing/placeholders.rs @@ -37,7 +37,7 @@ impl ProcessPlaceholders for Document { /// parses all placeholders and assigns values to them fn process_placeholders(&mut self) { self.placeholders.iter().for_each(|p| { - let mut pholder = p.lock().unwrap(); + let mut pholder = p.write().unwrap(); match pholder.name.to_lowercase().as_str() { P_TOC => { let ordered = if let Some(meta) = &pholder.metadata { @@ -74,7 +74,7 @@ impl ProcessPlaceholders for Document { let placeholders = self.placeholders.clone(); placeholders.iter().for_each(|p| { - let mut pholder = p.lock().unwrap(); + let mut pholder = p.write().unwrap(); let name = pholder.name.clone(); if let Some(cap) = RE_SET.captures(&name) { diff --git a/src/parsing/templates.rs b/src/parsing/templates.rs new file mode 100644 index 0000000..baf0526 --- /dev/null +++ b/src/parsing/templates.rs @@ -0,0 +1,291 @@ +use crate::parsing::elements::{Block, Element, Inline, Line, ListItem}; +use std::collections::HashMap; +use std::sync::{Arc, RwLock}; + +pub trait FreezeVariables { + fn freeze_variables(&mut self) -> Option<Arc<RwLock<TemplateVariable>>>; +} + +pub trait GetTemplateVariables { + fn get_template_variables(&self) -> Vec<Arc<RwLock<TemplateVariable>>>; +} + +#[derive(Clone, Debug)] +pub struct Template { + pub(crate) text: Vec<Element>, + pub(crate) variables: HashMap<String, Arc<RwLock<TemplateVariable>>>, +} + +#[derive(Clone, Debug)] +pub struct TemplateVariable { + pub(crate) prefix: String, + pub(crate) name: String, + pub(crate) suffix: String, + pub(crate) value: Option<Element>, +} + +impl Template { + pub fn render(&self, replacements: HashMap<String, Element>) -> Vec<Element> { + replacements.iter().for_each(|(k, r)| { + if let Some(v) = self.variables.get(k) { + v.write().unwrap().set_value(r.clone()) + } + }); + let elements = self + .text + .iter() + .map(|e| { + let mut e = e.clone(); + if let Some(template) = e.freeze_variables() { + Element::Inline(Box::new(Inline::TemplateVar(template))) + } else { + e + } + }) + .collect(); + self.variables + .iter() + .for_each(|(_, v)| v.write().unwrap().reset()); + + elements + } +} + +impl TemplateVariable { + pub fn set_value(&mut self, value: Element) { + self.value = Some(value) + } + pub fn reset(&mut self) { + self.value = None + } +} + +impl GetTemplateVariables for Inline { + fn get_template_variables(&self) -> Vec<Arc<RwLock<TemplateVariable>>> { + match self { + Inline::TemplateVar(temp) => vec![Arc::clone(temp)], + Inline::Colored(col) => col.value.get_template_variables(), + Inline::Superscript(sup) => sup.value.get_template_variables(), + Inline::Striked(striked) => striked.value.get_template_variables(), + Inline::Underlined(under) => under.value.get_template_variables(), + Inline::Italic(it) => it.value.get_template_variables(), + Inline::Bold(bo) => bo.value.get_template_variables(), + _ => Vec::new(), + } + } +} + +impl GetTemplateVariables for Line { + fn get_template_variables(&self) -> Vec<Arc<RwLock<TemplateVariable>>> { + match self { + Line::Text(line) => line + .subtext + .iter() + .map(|s| s.get_template_variables()) + .flatten() + .collect(), + Line::Centered(center) => center + .line + .subtext + .iter() + .map(|s| s.get_template_variables()) + .flatten() + .collect(), + _ => Vec::new(), + } + } +} + +impl GetTemplateVariables for Block { + fn get_template_variables(&self) -> Vec<Arc<RwLock<TemplateVariable>>> { + match self { + Block::Section(sec) => sec + .elements + .iter() + .map(|e| e.get_template_variables()) + .flatten() + .collect(), + Block::Paragraph(par) => par + .elements + .iter() + .map(|l| l.get_template_variables()) + .flatten() + .collect(), + Block::Quote(q) => q + .text + .iter() + .map(|t| { + t.subtext + .iter() + .map(|i| i.get_template_variables()) + .flatten() + }) + .flatten() + .collect(), + Block::List(list) => list + .items + .iter() + .map(|item| item.get_template_variables()) + .flatten() + .collect(), + _ => Vec::new(), + } + } +} + +impl GetTemplateVariables for Element { + fn get_template_variables(&self) -> Vec<Arc<RwLock<TemplateVariable>>> { + match self { + Element::Block(block) => block.get_template_variables(), + Element::Inline(inline) => inline.get_template_variables(), + Element::Line(line) => line.get_template_variables(), + } + } +} + +impl FreezeVariables for Inline { + fn freeze_variables(&mut self) -> Option<Arc<RwLock<TemplateVariable>>> { + match self { + Inline::TemplateVar(temp) => { + let temp = temp.read().unwrap(); + return Some(Arc::new(RwLock::new((*temp).clone()))); + } + Inline::Colored(col) => { + if let Some(temp) = col.value.freeze_variables() { + col.value = Box::new(Inline::TemplateVar(temp)) + } + } + Inline::Superscript(sup) => { + if let Some(temp) = sup.value.freeze_variables() { + sup.value = Box::new(Inline::TemplateVar(temp)) + } + } + Inline::Striked(striked) => { + if let Some(temp) = striked.value.freeze_variables() { + striked.value = Box::new(Inline::TemplateVar(temp)) + } + } + Inline::Underlined(under) => { + if let Some(temp) = under.value.freeze_variables() { + under.value = Box::new(Inline::TemplateVar(temp)) + } + } + Inline::Italic(it) => { + if let Some(temp) = it.value.freeze_variables() { + it.value = Box::new(Inline::TemplateVar(temp)) + } + } + Inline::Bold(bo) => { + if let Some(temp) = bo.value.freeze_variables() { + bo.value = Box::new(Inline::TemplateVar(temp)) + } + } + _ => {} + } + None + } +} + +impl GetTemplateVariables for ListItem { + fn get_template_variables(&self) -> Vec<Arc<RwLock<TemplateVariable>>> { + let mut inner_vars: Vec<Arc<RwLock<TemplateVariable>>> = self + .children + .iter() + .map(|child| child.get_template_variables()) + .flatten() + .collect(); + inner_vars.append(&mut self.text.get_template_variables()); + + inner_vars + } +} + +impl FreezeVariables for Line { + fn freeze_variables(&mut self) -> Option<Arc<RwLock<TemplateVariable>>> { + match self { + Line::Text(text) => { + text.subtext = text + .subtext + .iter_mut() + .map(|i| { + if let Some(t) = i.freeze_variables() { + Inline::TemplateVar(t) + } else { + (*i).clone() + } + }) + .collect() + } + Line::Centered(center) => { + center.line.subtext = center + .line + .subtext + .iter_mut() + .map(|i| { + if let Some(t) = i.freeze_variables() { + Inline::TemplateVar(t) + } else { + (*i).clone() + } + }) + .collect() + } + _ => {} + } + None + } +} + +impl FreezeVariables for Block { + fn freeze_variables(&mut self) -> Option<Arc<RwLock<TemplateVariable>>> { + match self { + Block::Section(s) => s.elements.iter_mut().for_each(|b| { + b.freeze_variables(); + }), + Block::Paragraph(p) => p.elements.iter_mut().for_each(|l| { + l.freeze_variables(); + }), + Block::Quote(q) => q.text.iter_mut().for_each(|t| { + t.subtext = t + .subtext + .iter_mut() + .map(|i| { + if let Some(t) = i.freeze_variables() { + Inline::TemplateVar(t) + } else { + (*i).clone() + } + }) + .collect() + }), + Block::List(list) => list.items.iter_mut().for_each(|item| { + item.freeze_variables(); + }), + _ => {} + }; + + None + } +} + +impl FreezeVariables for Element { + fn freeze_variables(&mut self) -> Option<Arc<RwLock<TemplateVariable>>> { + match self { + Element::Block(b) => b.freeze_variables(), + Element::Line(l) => l.freeze_variables(), + Element::Inline(i) => return i.freeze_variables(), + }; + + None + } +} + +impl FreezeVariables for ListItem { + fn freeze_variables(&mut self) -> Option<Arc<RwLock<TemplateVariable>>> { + self.children.iter_mut().for_each(|child| { + child.freeze_variables(); + }); + self.text.freeze_variables(); + None + } +}