diff --git a/Cargo.lock b/Cargo.lock index a62c42e..575734d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1142,7 +1142,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "snekdown" -version = "0.26.1" +version = "0.26.2" dependencies = [ "asciimath-rs 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index b5f5306..b39657a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "snekdown" -version = "0.26.1" +version = "0.26.2" authors = ["trivernis "] edition = "2018" license-file = "LICENSE" diff --git a/README.md b/README.md index 3ed589f..bc51965 100644 --- a/README.md +++ b/README.md @@ -72,9 +72,17 @@ Imports are parsed via multithreading. <[document.md] -<[style.css] +<[style.css][type=stylesheet] ``` +The parser differentiates four different types of imported files. + +- `document` - The default import which is just another snekdown document +- `stylesheet` - CSS Stylesheets that are inclued when rendering +- `bibliography` - A file including bibliography +- `config`/`manifest` - A config file that contains metadata + +If no type is provided the parser guesses the type of file from the extension. ### Tables @@ -133,6 +141,26 @@ Placeholder [key = [[placeholder]]] ``` +Metadata can also be defined in a separate toml file with simple key-value pairs. +Example: + +```toml +# bibliography.bib.toml +[Meta] +author = "Snek" +published = "2020" +test-key = ["test value", "test value 2"] + +[imports] +ignored-imports = ["style.css"] # those files won't get imported +included-stylesheets = ["style2.css"] # stylesheets that should be included +included-configs = [] # other metadata files that should be included +included-bibliography = ["nextbib.toml"]# bibliography that should be included +``` + +The `[Section]` keys are not relevant as the structure gets flattened before the values are read. + + #### Usage ``` @@ -187,6 +215,7 @@ There is a book about snekdown[^book] and a github repo[^github]. Entries can also be defined in a separate toml file with the following data layout: ```toml +# snekdown.toml [BIB_KEY] key = "value" @@ -233,7 +262,7 @@ The end goal is to have a markup language with features similar to LaTeX. - [x] Emojis (\:emoji:) - [x] Colors - [x] Watching and rendering on change -- [ ] Metadata files +- [x] Metadata files - [x] Bibliography - [x] Math - [ ] Text sizes diff --git a/src/parser/block.rs b/src/parser/block.rs index b1bb773..60ee3b0 100644 --- a/src/parser/block.rs +++ b/src/parser/block.rs @@ -328,7 +328,7 @@ impl ParseBlock for Parser { self.ctm.seek_whitespace(); - match self.import(path.clone(), metadata) { + match self.import(path.clone(), &metadata) { ImportType::Document(Ok(anchor)) => Ok(Some(Import { path, anchor })), ImportType::Stylesheet(_) => Ok(None), ImportType::Bibliography(_) => Ok(None), diff --git a/src/parser/mod.rs b/src/parser/mod.rs index de027da..d6eb1e6 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -4,6 +4,9 @@ pub(crate) mod line; use self::block::ParseBlock; use crate::elements::{Document, ImportAnchor}; +use crate::references::configuration::keys::{ + IMP_BIBLIOGRAPHY, IMP_CONFIGS, IMP_IGNORE, IMP_STYLESHEETS, +}; use crate::references::configuration::{Configuration, Value}; use bibliographix::bib_manager::BibManager; use charred::tapemachine::{CharTapeMachine, TapeError, TapeResult}; @@ -25,7 +28,7 @@ const DEFAULT_IMPORTS: &'static [(&str, &str)] = &[ ("snekdown.toml", "manifest"), ("manifest.toml", "manifest"), ("bibliography.toml", "bibliography"), - ("bibliography.bib.toml", "bibliography"), + ("bibliography2.bib.toml", "bibliography"), ("style.css", "stylesheet"), ]; @@ -248,8 +251,27 @@ impl Parser { } /// Imports a path - fn import(&mut self, path: String, args: HashMap) -> ImportType { + fn import(&mut self, path: String, args: &HashMap) -> ImportType { let path = self.transform_path(path); + if let Some(fname) = path + .file_name() + .and_then(|f| Some(f.to_str().unwrap().to_string())) + { + if let Some(Value::Array(ignore)) = self + .document + .config + .get_entry(IMP_IGNORE) + .and_then(|e| Some(e.get().clone())) + { + let ignore = ignore + .iter() + .map(|v| v.as_string()) + .collect::>(); + if ignore.contains(&fname) { + return ImportType::None; + } + } + } match args.get("type").map(|e| e.as_string().to_lowercase()) { Some(s) if s == "stylesheet".to_string() => { ImportType::Stylesheet(self.import_stylesheet(path)) @@ -308,17 +330,59 @@ impl Parser { for (path, file_type) in DEFAULT_IMPORTS { self.import( path.to_string(), - maplit::hashmap! {"type".to_string() => Value::String(file_type.to_string())}, + &maplit::hashmap! {"type".to_string() => Value::String(file_type.to_string())}, ); } } wg.wait(); + if !self.is_child { + self.import_from_config(); + } self.document.post_process(); let document = self.document.clone(); self.document = Document::new(!self.is_child); document } + + /// Imports files from the configs import values + fn import_from_config(&mut self) { + if let Some(Value::Array(mut imp)) = self + .document + .config + .get_entry(IMP_STYLESHEETS) + .and_then(|e| Some(e.get().clone())) + { + let args = + maplit::hashmap! {"type".to_string() => Value::String("stylesheet".to_string())}; + while let Some(Value::String(s)) = imp.pop() { + self.import(s, &args); + } + } + if let Some(Value::Array(mut imp)) = self + .document + .config + .get_entry(IMP_CONFIGS) + .and_then(|e| Some(e.get().clone())) + { + let args = maplit::hashmap! {"type".to_string() => Value::String("config".to_string())}; + while let Some(Value::String(s)) = imp.pop() { + self.import(s, &args); + } + } + if let Some(Value::Array(mut imp)) = self + .document + .config + .get_entry(IMP_BIBLIOGRAPHY) + .and_then(|e| Some(e.get().clone())) + { + let args = + maplit::hashmap! {"type".to_string() => Value::String("bibliography".to_string())}; + while let Some(Value::String(s)) = imp.pop() { + self.import(s, &args); + } + } + } } pub(crate) enum ImportType { @@ -326,4 +390,5 @@ pub(crate) enum ImportType { Stylesheet(ParseResult<()>), Bibliography(ParseResult<()>), Manifest(ParseResult<()>), + None, } diff --git a/src/references/configuration/keys.rs b/src/references/configuration/keys.rs index 37636b9..31de74b 100644 --- a/src/references/configuration/keys.rs +++ b/src/references/configuration/keys.rs @@ -1,2 +1,7 @@ pub const BIB_REF_DISPLAY: &str = "bib-ref-display"; pub const META_LANG: &str = "language"; + +pub const IMP_IGNORE: &str = "ignored-imports"; +pub const IMP_STYLESHEETS: &str = "included-stylesheets"; +pub const IMP_CONFIGS: &str = "included-configs"; +pub const IMP_BIBLIOGRAPHY: &str = "included-bibliography"; diff --git a/src/references/configuration/mod.rs b/src/references/configuration/mod.rs index 6e2ac9c..44ede9a 100644 --- a/src/references/configuration/mod.rs +++ b/src/references/configuration/mod.rs @@ -1,6 +1,7 @@ use crate::elements::MetadataValue; use crate::references::configuration::keys::{BIB_REF_DISPLAY, META_LANG}; use crate::references::templates::Template; +use serde::export::TryFrom; use std::collections::HashMap; use std::sync::{Arc, RwLock}; @@ -13,6 +14,7 @@ pub enum Value { Float(f64), Integer(i64), Template(Template), + Array(Vec), } #[derive(Clone, Debug)] @@ -34,6 +36,9 @@ impl Value { Value::Integer(int) => format!("{}", int), Value::Float(f) => format!("{:02}", f), Value::Bool(b) => format!("{}", b), + Value::Array(a) => a.iter().fold("".to_string(), |a, b| { + format!("{} \"{}\"", a, b.as_string()) + }), _ => "".to_string(), } } @@ -117,19 +122,35 @@ impl Configuration { } pub fn set_from_toml(&mut self, value: &toml::Value) -> Option<()> { - let table = value.as_table()?; + let table = value.as_table().cloned()?; table.iter().for_each(|(k, v)| { match v { toml::Value::Table(_) => self.set_from_toml(v).unwrap_or(()), - toml::Value::Float(f) => self.set(k, Value::Float(*f)), - toml::Value::Integer(i) => self.set(k, Value::Integer(*i)), - toml::Value::String(s) => self.set(k, Value::String(s.clone())), - toml::Value::Boolean(b) => self.set(k, Value::Bool(*b)), - toml::Value::Datetime(dt) => self.set(k, Value::String(dt.to_string())), - _ => {} + _ => self.set(k, Value::try_from(v.clone()).unwrap()), }; }); Some(()) } } + +impl TryFrom for Value { + type Error = (); + + fn try_from(value: toml::Value) -> Result { + match value { + toml::Value::Table(_) => Err(()), + toml::Value::Float(f) => Ok(Value::Float(f)), + toml::Value::Integer(i) => Ok(Value::Integer(i)), + toml::Value::String(s) => Ok(Value::String(s)), + toml::Value::Boolean(b) => Ok(Value::Bool(b)), + toml::Value::Datetime(dt) => Ok(Value::String(dt.to_string())), + toml::Value::Array(a) => Ok(Value::Array( + a.iter() + .cloned() + .filter_map(|e| Value::try_from(e).ok()) + .collect::>(), + )), + } + } +}