You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
snekdown/src/parsing/placeholders.rs

244 lines
8.1 KiB
Rust

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<Mutex<BibEntry>>;
fn get_bib_entry(&self, ph: &MutexGuard<Placeholder>, 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<String>,
date: Option<String>,
url: Option<String>,
title: Option<String>,
publisher: Option<String>,
notes: Option<String>,
display: Option<Arc<Mutex<ConfigValue>>>,
}
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<Mutex<BibEntry>> {
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<Placeholder>, 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())
}