Change bibliography to an easier concept

Definitions are similar to the markdown key-url definitions:
[key]:url
[key]:[metadata]

References work like some markdown-footnote concepts:

[^key]
pull/1/head
trivernis 5 years ago
parent 1eea0e3b86
commit 8498f4c66c

20
Cargo.lock generated

@ -566,7 +566,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "snekdown"
version = "0.13.0"
version = "0.14.0"
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)",
@ -577,8 +577,11 @@ dependencies = [
"minify 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"notify 4.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"syntect 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -646,7 +649,7 @@ dependencies = [
"serde_derive 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"yaml-rust 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -674,6 +677,14 @@ dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "toml"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-segmentation"
version = "1.6.0"
@ -762,7 +773,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "yaml-rust"
version = "0.4.3"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"linked-hash-map 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -848,6 +859,7 @@ dependencies = [
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
"checksum time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
"checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
@ -862,4 +874,4 @@ dependencies = [
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
"checksum xml-rs 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a"
"checksum yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d"
"checksum yaml-rust 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d"

@ -1,6 +1,6 @@
[package]
name = "snekdown"
version = "0.13.0"
version = "0.14.0"
authors = ["trivernis <trivernis@protonmail.com>"]
edition = "2018"
license-file = "LICENSE"
@ -27,4 +27,7 @@ regex = "1.3.9"
lazy_static = "1.4.0"
colored = "1.9.3"
gh-emoji = "1.0.3"
notify = "4.0.12"
notify = "4.0.12"
toml = "0.5.6"
serde ="1.0.111"
serde_derive = "1.0.111"

@ -166,6 +166,23 @@ _Underlined_
§[#0C0]Colored text§[] §[red] red §[]
```
## Bibliography
Bibliography entries can be defined and referenced anywhere in the document.
Definition:
```md
[book]:[author=Snek title = "Snekdown Book"]
[github]: https://github.com/trivernis/snekdown
```
Usage:
```
There is a book about snekdown[^book] and a github repo[^github].
```
Bibliography entries are only shown when used in the document.
## Roadmap
The end goal is to have a markup language with features similar to LaTeX.
@ -175,7 +192,7 @@ The end goal is to have a markup language with features similar to LaTeX.
- [x] Colors
- [x] Watching and rendering on change
- [ ] Metadata files
- [ ] Bibliography
- [x] Bibliography
- [ ] Math
- [ ] Text sizes
- [ ] Title pages

@ -1,4 +1,5 @@
use crate::format::Template;
use crate::parsing::bibliography::{BibEntry, BibReference};
use crate::parsing::elements::*;
use htmlescape::{encode_attribute, encode_minimal};
use minify::html::minify;
@ -38,7 +39,7 @@ impl ToHtml for Line {
Line::Ruler(ruler) => ruler.to_html(),
Line::Anchor(anchor) => anchor.to_html(),
Line::Centered(centered) => centered.to_html(),
Line::ReferenceEntry(ref_entry) => ref_entry.to_html(),
Line::BibEntry(bib) => bib.lock().unwrap().to_html(),
}
}
}
@ -55,11 +56,11 @@ impl ToHtml for Inline {
Inline::Bold(bold) => bold.to_html(),
Inline::Image(img) => img.to_html(),
Inline::Placeholder(placeholder) => placeholder.lock().unwrap().to_html(),
Inline::Reference(reference) => reference.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(),
}
}
}
@ -435,51 +436,6 @@ impl ToHtml for Centered {
}
}
impl ToHtml for Reference {
fn to_html(&self) -> String {
if let Some(value) = &self.value {
let ref_id = value.get_ref_id();
if let Some(display) = &self.display {
match value {
RefValue::BibEntry(bib) => {
let bib = bib.lock().unwrap();
let mut template = bib.get_template();
template.set_value(display.lock().unwrap().value.to_html());
format!("<a href='#{}'>{}</a>", ref_id, template.render())
}
}
} else {
format!("<a href='#{}'>{}</a>", ref_id, value.to_html())
}
} else {
"Unknown reference".to_string()
}
}
}
impl ToHtml for RefValue {
fn to_html(&self) -> String {
match self {
RefValue::BibEntry(bib) => encode_minimal(bib.lock().unwrap().get_formatted().as_str()),
}
}
}
impl ToHtml for ReferenceEntry {
fn to_html(&self) -> String {
if let Some(val) = &self.value {
format!(
"<div id='{}'>{}</div>",
encode_attribute(val.get_ref_id().as_str()),
val.to_html()
)
} else {
"Unknown reference".to_string()
}
}
}
impl ToHtml for Checkbox {
fn to_html(&self) -> String {
if self.value {
@ -509,3 +465,44 @@ impl ToHtml for Colored {
)
}
}
impl ToHtml for BibReference {
fn to_html(&self) -> String {
format!(
"<sup><a href='#{}'>{}</a></sup>",
self.key.clone(),
self.get_formatted()
)
}
}
impl ToHtml for BibEntry {
fn to_html(&self) -> String {
if !self.is_visible() {
return "".to_string();
}
if let Some(display) = &self.display {
let display = display.lock().unwrap();
let mut template = Template::new(display.get().as_string());
template.set_replacements(self.as_map());
format!(
"<span id='{}'>{}</span>",
encode_attribute(self.key.as_str()),
encode_minimal(template.render().as_str())
)
} else {
if let Some(url) = &self.url {
format!(
"<a id={1} href='{0}'>{1}</a>",
encode_attribute(url.as_str()),
encode_minimal(self.key.as_str())
)
} else {
format!(
"<span id='{0}'><u>{0}</u></span>",
encode_attribute(self.key.as_str())
)
}
}
}
}

@ -0,0 +1,200 @@
use crate::format::Template;
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};
const B_NUMBER: &str = "number";
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) number: usize,
pub(crate) ref_count: usize,
pub key: String,
pub author: Option<String>,
pub date: Option<String>,
pub url: Option<String>,
pub title: Option<String>,
pub publisher: Option<String>,
pub notes: Option<String>,
pub display: Option<ConfigRefEntry>,
pub hide_unused: Option<ConfigRefEntry>,
}
#[derive(Clone, Debug)]
pub struct BibReference {
pub(crate) key: String,
pub(crate) reference_entry: Option<Arc<Mutex<BibEntry>>>,
pub(crate) display: Option<ConfigRefEntry>,
}
#[derive(Clone, Debug)]
pub struct Bibliography {
entries: HashMap<String, Arc<Mutex<BibEntry>>>,
references: Vec<Arc<Mutex<BibReference>>>,
}
impl BibEntry {
pub fn as_map(&self) -> HashMap<String, String> {
let mut map = HashMap::new();
map.insert(B_NUMBER.to_string(), format!("{}", self.number));
map.insert("key".to_string(), self.key.clone());
if let Some(author) = &self.author {
map.insert(B_AUTHOR.to_string(), author.clone());
}
if let Some(date) = &self.date {
map.insert(B_DATE.to_string(), date.clone());
}
if let Some(url) = &self.url {
map.insert(B_URL.to_string(), url.clone());
}
if let Some(title) = &self.title {
map.insert(B_TITLE.to_string(), title.clone());
}
if let Some(publisher) = &self.publisher {
map.insert(B_PUBLISHER.to_string(), publisher.clone());
}
if let Some(notes) = &self.notes {
map.insert(B_NOTES.to_string(), notes.clone());
}
map
}
pub fn from_metadata(key: String, data: Box<dyn Metadata>, config: &Configuration) -> Self {
BibEntry {
number: 0,
ref_count: 0,
key,
author: data.get_string(B_AUTHOR),
date: data.get_string(B_DATE),
url: data.get_string(B_URL),
title: data.get_string(B_TITLE),
publisher: data.get_string(B_PUBLISHER),
notes: data.get_string(B_NOTES),
display: config.get_ref_entry(BIB_DISPLAY),
hide_unused: config.get_ref_entry(BIB_HIDE_UNUSED),
}
}
pub fn from_url(key: String, url: String, config: &Configuration) -> Self {
BibEntry {
number: 0,
ref_count: 0,
key,
author: None,
date: None,
url: Some(url),
title: None,
publisher: None,
notes: None,
display: config.get_ref_entry(BIB_DISPLAY),
hide_unused: config.get_ref_entry(BIB_HIDE_UNUSED),
}
}
pub fn set_number(&mut self, number: usize) {
self.number = number
}
pub fn set_ref_count(&mut self, number: usize) {
self.ref_count = number
}
pub fn is_visible(&self) -> bool {
if let Some(hide_cfg) = &self.hide_unused {
let hide_cfg = hide_cfg.lock().unwrap();
if let Value::Bool(b) = hide_cfg.get() {
if *b && self.ref_count == 0 {
return false;
}
}
}
true
}
}
impl BibReference {
pub fn new(key: String, display: Option<ConfigRefEntry>) -> Self {
Self {
key: key.to_string(),
display,
reference_entry: None,
}
}
/// sets the reference to the bib entry
pub(crate) fn set_entry(&mut self, entry: Arc<Mutex<BibEntry>>) {
self.reference_entry = Some(entry)
}
pub(crate) fn get_formatted(&self) -> String {
if let Some(entry) = &self.reference_entry {
let entry = entry.lock().unwrap();
if let Some(display) = &self.display {
let display = display.lock().unwrap();
let mut template = Template::new(display.get().as_string());
template.set_replacements(entry.as_map());
return template.render();
}
return format!("{}", entry.number);
}
return "citation needed".to_string();
}
}
impl Bibliography {
pub fn new() -> Self {
Self {
entries: HashMap::new(),
references: Vec::new(),
}
}
pub(crate) fn assign_entry_data(&mut self) {
let mut count = 0;
self.references.iter().for_each(|e| {
let mut reference = e.lock().unwrap();
if let Some(entry) = self.entries.get(&reference.key) {
{
let mut entry_raw = entry.lock().unwrap();
let ref_count = entry_raw.ref_count;
entry_raw.set_ref_count(ref_count + 1);
}
reference.set_entry(Arc::clone(entry));
}
});
self.entries.iter().for_each(|(_, e)| {
let mut entry = e.lock().unwrap();
if entry.is_visible() {
count += 1;
entry.set_number(count)
}
});
}
pub fn add_ref_entry(&mut self, entry: Arc<Mutex<BibReference>>) {
self.references.push(entry)
}
pub fn add_bib_entry(&mut self, entry: Arc<Mutex<BibEntry>>) {
let key = entry.lock().unwrap().key.clone();
self.entries.insert(key, entry);
}
pub fn combine(&mut self, other: &mut Bibliography) {
let other_entries = other.entries.clone();
other.entries = HashMap::new();
self.entries.extend(other_entries.into_iter());
self.references.append(&mut other.references);
}
}

@ -0,0 +1,21 @@
use serde_derive::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RootConfig {
pub(crate) bibliography: Option<BibConfig>,
pub(crate) metadata: Option<MetaConfig>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BibConfig {
pub(crate) entry_display: Option<String>,
pub(crate) reference_display: Option<String>,
pub(crate) hide_unused: Option<bool>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MetaConfig {
pub(crate) author: Option<String>,
pub(crate) date: Option<String>,
pub(crate) title: Option<String>,
}

@ -0,0 +1,4 @@
[bibliography]
entry_display = "{{number}}: {{author}} - {{title}} - {{date}} - {{url}}"
reference_display = "{{number}}"
hide_unused = true

@ -0,0 +1,7 @@
pub const BIB_DISPLAY: &str = "bib-entry-display";
pub const BIB_REF_DISPLAY: &str = "bib-ref-display";
pub const BIB_HIDE_UNUSED: &str = "bib-hide-unused";
pub const META_AUTHOR: &str = "author";
pub const META_TITLE: &str = "title";
pub const META_DATE: &str = "date";

@ -0,0 +1,141 @@
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;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
pub mod config;
pub(crate) mod keys;
#[derive(Clone, Debug)]
pub enum Value {
String(String),
Bool(bool),
Float(f64),
Integer(i64),
}
#[derive(Clone, Debug)]
pub struct ConfigEntry {
inner: Value,
}
pub type ConfigRefEntry = Arc<Mutex<ConfigEntry>>;
#[derive(Clone, Debug)]
pub struct Configuration {
config: Arc<Mutex<HashMap<String, ConfigRefEntry>>>,
}
impl Value {
pub fn as_string(&self) -> String {
match self {
Value::String(string) => string.clone(),
Value::Integer(int) => format!("{}", int),
Value::Float(f) => format!("{:02}", f),
Value::Bool(b) => format!("{}", b),
}
}
}
impl ConfigEntry {
pub fn new(value: Value) -> Self {
Self { inner: value }
}
pub fn set(&mut self, value: Value) {
self.inner = value;
}
pub fn get(&self) -> &Value {
&self.inner
}
}
impl Configuration {
pub fn new() -> Self {
Self {
config: Arc::new(Mutex::new(HashMap::new())),
}
}
pub fn default() -> Self {
let mut self_config = Self::new();
lazy_static::lazy_static! { static ref CONFIG: RootConfig = toml::from_str(std::include_str!("default.toml")).unwrap();}
self_config.assign_config(&CONFIG);
self_config
}
pub fn assign_config(&mut self, config: &RootConfig) {
if let Some(bib) = &config.bibliography {
if let Some(cfg) = &bib.entry_display {
self.set(BIB_DISPLAY, Value::String(cfg.clone()))
}
if let Some(cfg) = &bib.reference_display {
self.set(BIB_REF_DISPLAY, Value::String(cfg.clone()))
}
if let Some(cfg) = &bib.hide_unused {
self.set(BIB_HIDE_UNUSED, Value::Bool(*cfg));
}
}
if let Some(meta) = &config.metadata {
if let Some(cfg) = &meta.author {
self.set(META_AUTHOR, Value::String(cfg.clone()))
}
if let Some(cfg) = &meta.date {
self.set(META_DATE, Value::String(cfg.clone()))
}
if let Some(cfg) = &meta.title {
self.set(META_TITLE, Value::String(cfg.clone()))
}
}
}
/// returns the value of a config entry
pub fn get_entry(&self, key: &str) -> Option<ConfigEntry> {
let config = self.config.lock().unwrap();
if let Some(entry) = config.get(key) {
let value = entry.lock().unwrap();
Some(value.clone())
} else {
None
}
}
/// returns a config entry that is a reference to a value
pub fn get_ref_entry(&self, key: &str) -> Option<ConfigRefEntry> {
let config = self.config.lock().unwrap();
if let Some(entry) = config.get(&key.to_string()) {
Some(Arc::clone(entry))
} else {
None
}
}
/// Sets a config parameter
pub fn set(&mut self, key: &str, value: Value) {
let mut config = self.config.lock().unwrap();
if let Some(entry) = config.get(&key.to_string()) {
entry.lock().unwrap().set(value)
} else {
config.insert(
key.to_string(),
Arc::new(Mutex::new(ConfigEntry::new(value))),
);
}
}
/// Sets a config value based on a metadata value
pub fn set_from_meta(&mut self, key: &str, value: MetadataValue) {
match value {
MetadataValue::String(string) => self.set(key, Value::String(string)),
MetadataValue::Bool(bool) => self.set(key, Value::Bool(bool)),
MetadataValue::Float(f) => self.set(key, Value::Float(f)),
MetadataValue::Integer(i) => self.set(key, Value::Integer(i)),
_ => {}
}
}
}

@ -1,4 +1,6 @@
use crate::parsing::placeholders::BibEntry;
use crate::parsing::bibliography::{BibEntry, BibReference, Bibliography};
use crate::parsing::configuration::Configuration;
use crate::parsing::placeholders::ProcessPlaceholders;
use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
@ -45,7 +47,7 @@ pub enum Line {
Ruler(Ruler),
Anchor(Anchor),
Centered(Centered),
ReferenceEntry(ReferenceEntry),
BibEntry(Arc<Mutex<BibEntry>>),
}
#[derive(Clone, Debug)]
@ -54,8 +56,8 @@ pub struct Document {
pub(crate) is_root: bool,
pub(crate) path: Option<String>,
pub(crate) placeholders: Vec<Arc<Mutex<Placeholder>>>,
pub bib_entries: HashMap<String, Arc<Mutex<BibEntry>>>,
pub config_settings: HashMap<String, Arc<Mutex<ConfigValue>>>,
pub config: Configuration,
pub bibliography: Bibliography,
}
#[derive(Clone, Debug)]
@ -155,10 +157,10 @@ pub enum Inline {
Url(Url),
Image(Image),
Placeholder(Arc<Mutex<Placeholder>>),
Reference(Reference),
Checkbox(Checkbox),
Emoji(Emoji),
Colored(Colored),
BibReference(Arc<Mutex<BibReference>>),
}
#[derive(Clone, Debug)]
@ -231,29 +233,6 @@ pub struct Centered {
pub(crate) line: TextLine,
}
#[derive(Clone, Debug)]
pub struct Reference {
pub(crate) value: Option<RefValue>,
pub(crate) metadata: Option<InlineMetadata>,
pub(crate) display: Option<Arc<Mutex<ConfigValue>>>,
}
#[derive(Clone, Debug)]
pub struct ReferenceEntry {
pub(crate) value: Option<RefValue>,
pub(crate) reference_count: usize,
}
#[derive(Clone, Debug)]
pub enum RefValue {
BibEntry(Arc<Mutex<BibEntry>>),
}
#[derive(Clone, Debug)]
pub struct ConfigValue {
pub(crate) value: MetadataValue,
}
#[derive(Clone, Debug)]
pub struct Emoji {
pub(crate) value: char,
@ -269,34 +248,14 @@ pub struct Colored {
// implementations
impl Document {
fn get_default_config() -> HashMap<String, Arc<Mutex<ConfigValue>>> {
let mut config: HashMap<String, Arc<Mutex<ConfigValue>>> = HashMap::new();
config.insert(
"bib-display".to_string(),
Arc::new(Mutex::new(ConfigValue {
value: MetadataValue::String(
"{{title}} - {{author}} - {{date}} - {{url}} - ({{notes}})".to_string(),
),
})),
);
config.insert(
"ref-display".to_string(),
Arc::new(Mutex::new(ConfigValue {
value: MetadataValue::String("[{{key}}]".to_string()),
})),
);
config
}
pub fn new(is_root: bool) -> Self {
Self {
elements: Vec::new(),
is_root,
path: None,
placeholders: Vec::new(),
bib_entries: HashMap::new(),
config_settings: Self::get_default_config(),
config: Configuration::default(),
bibliography: Bibliography::new(),
}
}
@ -331,29 +290,6 @@ impl Document {
list
}
pub fn set_config_param(
&mut self,
key: String,
value: ConfigValue,
) -> Option<Arc<Mutex<ConfigValue>>> {
if let Some(arc_val) = self.config_settings.get(&key) {
arc_val.lock().unwrap().set_value(value.value);
Some(Arc::clone(&arc_val))
} else {
self.config_settings
.insert(key, Arc::new(Mutex::new(value)))
}
}
pub fn get_config_param(&self, key: &str) -> Option<Arc<Mutex<ConfigValue>>> {
if let Some(val) = self.config_settings.get(key) {
Some(Arc::clone(val))
} else {
None
}
}
/// Processes section and import elements
///
/// if it encounters a section it checks if the sections is of smaller order than the previous one
@ -385,6 +321,7 @@ impl Document {
let anchor = &mut arc_anchor.lock().unwrap();
if let Some(doc) = &mut anchor.document {
self.placeholders.append(&mut doc.placeholders);
self.bibliography.combine(&mut doc.bibliography);
doc.elements.reverse();
self.elements.append(&mut doc.elements);
anchor.document = None;
@ -408,11 +345,14 @@ impl Document {
}
self.elements = new_order;
}
}
impl ConfigValue {
fn set_value(&mut self, value: MetadataValue) {
self.value = value;
pub fn post_process(&mut self) {
self.postprocess_imports();
if self.is_root {
self.process_definitions();
self.bibliography.assign_entry_data();
self.process_placeholders();
}
}
}
@ -630,8 +570,13 @@ impl Placeholder {
}
}
impl InlineMetadata {
pub fn get_bool(&self, key: &str) -> bool {
pub trait Metadata {
fn get_bool(&self, key: &str) -> bool;
fn get_string(&self, key: &str) -> Option<String>;
}
impl Metadata for InlineMetadata {
fn get_bool(&self, key: &str) -> bool {
if let Some(MetadataValue::Bool(value)) = self.data.get(key) {
*value
} else {
@ -639,7 +584,7 @@ impl InlineMetadata {
}
}
pub fn get_string(&self, key: &str) -> Option<String> {
fn get_string(&self, key: &str) -> Option<String> {
if let Some(MetadataValue::String(value)) = self.data.get(key) {
Some(value.clone())
} else {
@ -647,17 +592,3 @@ impl InlineMetadata {
}
}
}
impl RefValue {
pub fn get_ref_id(&self) -> String {
match self {
RefValue::BibEntry(bib) => {
let bib = bib.lock().unwrap();
let mut key = bib.key.clone();
key.retain(|c| !c.is_whitespace());
key
}
}
}
}

@ -1,8 +1,11 @@
use super::charstate::CharStateMachine;
use super::elements::*;
use super::tokens::*;
use crate::parsing::bibliography::BibReference;
use crate::parsing::configuration::keys::BIB_REF_DISPLAY;
use crate::parsing::utils::{ParseError, ParseResult};
use crate::Parser;
use std::sync::{Arc, Mutex};
pub(crate) trait ParseInline {
fn parse_surrounded(&mut self, surrounding: &char) -> ParseResult<Inline>;
@ -18,10 +21,23 @@ pub(crate) trait ParseInline {
fn parse_superscript(&mut self) -> ParseResult<SuperscriptText>;
fn parse_emoji(&mut self) -> ParseResult<Emoji>;
fn parse_colored(&mut self) -> ParseResult<Colored>;
fn parse_bibref(&mut self) -> ParseResult<Arc<Mutex<BibReference>>>;
fn parse_plain(&mut self) -> ParseResult<PlainText>;
}
impl ParseInline for Parser {
/// parses Inline surrounded by characters
fn parse_surrounded(&mut self, surrounding: &char) -> ParseResult<Inline> {
let start_index = self.index;
self.assert_special(surrounding, start_index)?;
self.skip_char();
let inline = self.parse_inline()?;
self.assert_special(surrounding, start_index)?;
self.skip_char();
Ok(inline)
}
/// parses Inline, the formatting parts of a line (Text)
fn parse_inline(&mut self) -> ParseResult<Inline> {
if self.check_special(&PIPE) || self.check_linebreak() {
@ -52,6 +68,8 @@ impl ParseInline for Parser {
Ok(Inline::Emoji(emoji))
} else if let Ok(colored) = self.parse_colored() {
Ok(Inline::Colored(colored))
} else if let Ok(bibref) = self.parse_bibref() {
Ok(Inline::BibReference(bibref))
} else {
Ok(Inline::Plain(self.parse_plain()?))
}
@ -180,44 +198,6 @@ impl ParseInline for Parser {
})
}
/// parses plain text as a string until it encounters an unescaped special inline char
fn parse_plain(&mut self) -> ParseResult<PlainText> {
if self.check_linebreak() {
return Err(ParseError::new(self.index));
}
let mut characters = String::new();
characters.push(self.current_char);
while let Some(ch) = self.next_char() {
if self.check_special_group(&INLINE_SPECIAL_CHARS)
|| self.check_special_group(&self.inline_break_at)
{
break;
}
characters.push(ch)
}
if characters.len() > 0 {
Ok(PlainText { value: characters })
} else {
Err(ParseError::new_with_message(
self.index,
"no plaintext characters parsed",
))
}
}
/// parses Inline surrounded by characters
fn parse_surrounded(&mut self, surrounding: &char) -> ParseResult<Inline> {
let start_index = self.index;
self.assert_special(surrounding, start_index)?;
self.skip_char();
let inline = self.parse_inline()?;
self.assert_special(surrounding, start_index)?;
self.skip_char();
Ok(inline)
}
fn parse_emoji(&mut self) -> ParseResult<Emoji> {
let start_index = self.index;
self.assert_special(&EMOJI, start_index)?;
@ -251,4 +231,47 @@ impl ParseInline for Parser {
color,
})
}
fn parse_bibref(&mut self) -> ParseResult<Arc<Mutex<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(
key,
self.document.config.get_ref_entry(BIB_REF_DISPLAY),
)));
self.document
.bibliography
.add_ref_entry(Arc::clone(&ref_entry));
Ok(ref_entry)
}
/// parses plain text as a string until it encounters an unescaped special inline char
fn parse_plain(&mut self) -> ParseResult<PlainText> {
if self.check_linebreak() {
return Err(ParseError::new(self.index));
}
let mut characters = String::new();
characters.push(self.current_char);
while let Some(ch) = self.next_char() {
if self.check_special_group(&INLINE_SPECIAL_CHARS)
|| self.check_special_group(&self.inline_break_at)
{
break;
}
characters.push(ch)
}
if characters.len() > 0 {
Ok(PlainText { value: characters })
} else {
Err(ParseError::new_with_message(
self.index,
"no plaintext characters parsed",
))
}
}
}

@ -1,8 +1,11 @@
pub mod bibliography;
pub mod charstate;
pub mod configuration;
pub mod elements;
pub mod inline;
pub mod parser;
pub mod placeholders;
pub mod tokens;
#[macro_use]
pub mod utils;

@ -1,8 +1,8 @@
use super::elements::*;
use super::tokens::*;
use crate::parsing::bibliography::BibEntry;
use crate::parsing::charstate::CharStateMachine;
use crate::parsing::inline::ParseInline;
use crate::parsing::placeholders::ProcessPlaceholders;
use crate::parsing::utils::{ParseError, ParseResult};
use colored::*;
use crossbeam_utils::sync::WaitGroup;
@ -26,7 +26,7 @@ pub struct Parser {
wg: WaitGroup,
is_child: bool,
pub(crate) inline_break_at: Vec<char>,
document: Document,
pub(crate) document: Document,
pub(crate) previous_char: char,
pub(crate) reader: Box<dyn BufRead>,
}
@ -230,11 +230,7 @@ impl Parser {
let wg = self.wg.clone();
self.wg = WaitGroup::new();
wg.wait();
self.document.postprocess_imports();
if !self.is_child {
self.document.process_placeholders();
}
self.document.post_process();
let document = self.document.clone();
self.document = Document::new(!self.is_child);
@ -693,6 +689,8 @@ impl Parser {
Ok(Line::Ruler(ruler))
} else if let Ok(centered) = self.parse_centered() {
Ok(Line::Centered(centered))
} else if let Ok(bib) = self.parse_bib_entry() {
Ok(Line::BibEntry(bib))
} else if let Ok(text) = self.parse_text_line() {
Ok(Line::Text(text))
} else {
@ -701,6 +699,30 @@ impl Parser {
}
}
fn parse_bib_entry(&mut self) -> ParseResult<Arc<Mutex<BibEntry>>> {
let start_index = self.index;
self.seek_inline_whitespace();
self.assert_special(&BIB_KEY_OPEN, start_index)?;
self.skip_char();
let key = self.get_string_until_or_revert(&[BIB_KEY_CLOSE], &[LB, SPACE], start_index)?;
self.skip_char();
self.assert_special(&BIB_DATA_START, start_index)?;
self.skip_char();
self.seek_inline_whitespace();
let entry = if let Ok(meta) = self.parse_inline_metadata() {
BibEntry::from_metadata(key, Box::new(meta), &self.document.config)
} else {
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));
self.document
.bibliography
.add_bib_entry(Arc::clone(&entry_ref));
Ok(entry_ref)
}
/// parses centered text
fn parse_centered(&mut self) -> Result<Centered, ParseError> {
let start_index = self.index;

@ -1,8 +1,6 @@
use super::elements::*;
use crate::format::Template;
use chrono::prelude::*;
use regex::Regex;
use std::sync::{Arc, Mutex, MutexGuard};
macro_rules! block {
($inner:expr) => {
@ -26,71 +24,6 @@ macro_rules! inline {
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";
@ -103,28 +36,9 @@ 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() {
match pholder.name.to_lowercase().as_str() {
P_TOC => {
let ordered = if let Some(meta) = &pholder.metadata {
meta.get_bool("ordered")
@ -142,16 +56,18 @@ impl ProcessPlaceholders for Document {
P_DATETIME => pholder.set_value(inline!(Inline::Plain(PlainText {
value: format!("{} {}", get_date_string(), get_time_string())
}))),
_ => {}
_ => {
if let Some(entry) = self.config.get_entry(pholder.name.to_lowercase().as_str())
{
let value = entry.get().as_string();
pholder.set_value(inline!(Inline::Plain(PlainText { value })))
}
}
}
})
}
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();
}
@ -159,22 +75,7 @@ impl ProcessPlaceholders for Document {
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) {
@ -184,12 +85,7 @@ impl ProcessPlaceholders for Document {
})));
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(),
},
);
self.config.set_from_meta(key, value.clone())
}
}
}
@ -197,39 +93,6 @@ impl ProcessPlaceholders for Document {
}
});
}
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 {

@ -51,6 +51,12 @@ pub(crate) const CHECK_CHECKED: char = X;
pub(crate) const COLOR_START: char = PARAGRAPH;
pub(crate) const COLOR_OPEN: char = R_BRACKET;
pub(crate) const COLOR_CLOSE: char = L_BRACKET;
pub(crate) const BIBREF_OPEN: char = R_BRACKET;
pub(crate) const BIBREF_REF: char = UP;
pub(crate) const BIBREF_CLOSE: char = L_BRACKET;
pub(crate) const BIB_KEY_OPEN: char = R_BRACKET;
pub(crate) const BIB_KEY_CLOSE: char = L_BRACKET;
pub(crate) const BIB_DATA_START: char = COLON;
pub(crate) const ITALIC: char = ASTERISK;
pub(crate) const MONOSPACE: char = BACKTICK;
@ -102,6 +108,7 @@ pub(crate) const SQ_PHOLDER_START: [char; 2] = [PHOLDER_OPEN, PHOLDER_OPEN];
pub(crate) const SQ_PHOLDER_STOP: [char; 2] = [PHOLDER_CLOSE, PHOLDER_CLOSE];
pub(crate) const SQ_CENTERED_START: [char; 2] = [PIPE, PIPE];
pub(crate) const SQ_COLOR_START: [char; 2] = [COLOR_START, COLOR_OPEN];
pub(crate) const SQ_BIBREF_START: [char; 2] = [BIBREF_OPEN, BIBREF_REF];
// expressions

Loading…
Cancel
Save