use super::elements::*; use crate::format::Template; use chrono::prelude::*; use regex::Regex; use std::sync::{Arc, Mutex, MutexGuard}; macro_rules! block { ($inner:expr) => { Element::Block(Box::new($inner)) }; } #[allow(unused)] macro_rules! line { ($inner:expr) => { Element::Line(Box::new($inner)) }; } macro_rules! inline { ($inner:expr) => { Element::Inline(Box::new($inner)) }; } pub(crate) trait ProcessPlaceholders { fn process_placeholders(&mut self); fn process_definitions(&mut self); fn add_bib_entry(&mut self, key: String, value: BibEntry) -> Arc>; fn get_bib_entry(&self, ph: &MutexGuard, key: &str) -> BibEntry; } const B_AUTHOR: &str = "author"; const B_DATE: &str = "date"; const B_URL: &str = "url"; const B_TITLE: &str = "title"; const B_PUBLISHER: &str = "publisher"; const B_NOTES: &str = "notes"; #[derive(Clone, Debug)] pub struct BibEntry { pub(crate) key: String, author: Option, date: Option, url: Option, title: Option, publisher: Option, notes: Option, display: Option>>, } impl BibEntry { pub fn get_template(&self) -> Template { let mut template = Template::empty(); template.add_replacement("key", &self.key); if let Some(author) = &self.author { template.add_replacement(B_AUTHOR, author.as_str()); } if let Some(date) = &self.date { template.add_replacement(B_DATE, date.as_str()); } if let Some(url) = &self.url { template.add_replacement(B_URL, url.as_str()); } if let Some(title) = &self.title { template.add_replacement(B_TITLE, title.as_str()); } if let Some(publisher) = &self.publisher { template.add_replacement(B_PUBLISHER, publisher.as_str()); } if let Some(notes) = &self.notes { template.add_replacement(B_NOTES, notes.as_str()); } template } pub fn get_formatted(&self) -> String { if let Some(display) = &self.display { let value = display.lock().unwrap(); if let MetadataValue::String(format) = &value.value { let mut template = self.get_template(); template.set_value(format.clone()); template.render() } else { format!("'Invalid formatter!' {:?}", self) } } else { format!("{:?}", self) } } } const S_VALUE: &str = "value"; const P_TOC: &str = "toc"; const P_DATE: &str = "date"; const P_TIME: &str = "time"; const P_DATETIME: &str = "datetime"; impl ProcessPlaceholders for Document { /// parses all placeholders and assigns values to them fn process_placeholders(&mut self) { self.process_definitions(); lazy_static::lazy_static! {static ref RE_REF: Regex = Regex::new(r"^ref:(.*)$").unwrap();} self.placeholders.iter().for_each(|p| { let mut pholder = p.lock().unwrap(); if let Some(cap) = RE_REF.captures(&pholder.name) { if let Some(key) = cap.get(1) { if let Some(entry) = self.bib_entries.get(key.as_str()) { pholder.value = Some(inline!(Inline::Reference(Reference { value: Some(RefValue::BibEntry(entry.clone())), metadata: pholder.metadata.clone(), display: self.get_config_param("ref-display") }))) } else { pholder.value = Some(inline!(Inline::Reference(Reference { value: None, metadata: pholder.metadata.clone(), display: self.get_config_param("ref-display") }))) } } } match pholder.name.to_ascii_lowercase().as_str() { P_TOC => { let ordered = if let Some(meta) = &pholder.metadata { meta.get_bool("ordered") } else { false }; pholder.set_value(block!(Block::List(self.create_toc(ordered)))) } P_DATE => pholder.set_value(inline!(Inline::Plain(PlainText { value: get_date_string() }))), P_TIME => pholder.set_value(inline!(Inline::Plain(PlainText { value: get_time_string() }))), P_DATETIME => pholder.set_value(inline!(Inline::Plain(PlainText { value: format!("{} {}", get_date_string(), get_time_string()) }))), _ => {} } }) } fn process_definitions(&mut self) { lazy_static::lazy_static! { static ref RE_BIB: Regex = Regex::new(r"^bib:(.*)$").unwrap(); } lazy_static::lazy_static! { static ref RE_SET: Regex = Regex::new(r"^set:(.*)$").unwrap(); } let placeholders = self.placeholders.clone(); placeholders.iter().for_each(|p| { let mut pholder = p.lock().unwrap(); let name = pholder.name.clone(); if let Some(cap) = RE_BIB.captures(&name) { if let Some(key) = cap.get(1) { let key: &str = key.as_str(); let entry = self.get_bib_entry(&pholder, key); let entry = self.add_bib_entry(key.to_string(), entry); pholder.value = Some(Element::Line(Box::new(Line::ReferenceEntry( ReferenceEntry { value: Some(RefValue::BibEntry(entry)), reference_count: 0, }, )))); } return; } if let Some(cap) = RE_SET.captures(&name) { if let Some(key) = cap.get(1) { let key: &str = key.as_str(); pholder.value = Some(inline!(Inline::Plain(PlainText { value: "".to_string() }))); if let Some(meta) = &pholder.metadata { if let Some(value) = meta.data.get(S_VALUE) { self.set_config_param( key.to_string(), ConfigValue { value: value.clone(), }, ); } } } return; } }); } fn add_bib_entry(&mut self, key: String, value: BibEntry) -> Arc> { let arc_entry = Arc::new(Mutex::new(value)); self.bib_entries.insert(key, Arc::clone(&arc_entry)); arc_entry } fn get_bib_entry(&self, ph: &MutexGuard, key: &str) -> BibEntry { if let Some(meta) = &ph.metadata { BibEntry { key: key.to_string(), author: meta.get_string(B_AUTHOR), date: meta.get_string(B_DATE), url: meta.get_string(B_URL), title: meta.get_string(B_TITLE), publisher: meta.get_string(B_PUBLISHER), notes: meta.get_string(B_NOTES), display: self.get_config_param("bib-display"), } } else { BibEntry { key: key.to_string(), author: None, date: None, url: None, title: None, publisher: None, notes: None, display: self.get_config_param("bib-display"), } } } } fn get_time_string() -> String { let now = Local::now(); format!("{:02}:{:02}:{:02}", now.hour(), now.minute(), now.second()) } fn get_date_string() -> String { let now = Local::now(); format!("{:02}.{:02}.{:04}", now.day(), now.month(), now.year()) }