From f5bf361795dc96c67e76f69e5afb10117b65fab2 Mon Sep 17 00:00:00 2001 From: trivernis Date: Sat, 5 Sep 2020 19:04:33 +0200 Subject: [PATCH] Add metadata file imports and import types Signed-off-by: trivernis --- Cargo.lock | 3 +- Cargo.toml | 3 +- src/elements/mod.rs | 16 ++++- src/format/html/to_html.rs | 11 +-- src/parser/block.rs | 15 ++-- src/parser/mod.rs | 86 ++++++++++++++++++----- src/references/configuration/config.rs | 22 ------ src/references/configuration/default.toml | 4 -- src/references/configuration/keys.rs | 8 +-- src/references/configuration/mod.rs | 67 +++++++----------- 10 files changed, 131 insertions(+), 104 deletions(-) delete mode 100644 src/references/configuration/config.rs delete mode 100644 src/references/configuration/default.toml diff --git a/Cargo.lock b/Cargo.lock index bde2f5d..da59c91 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.25.0" +version = "0.26.0" 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)", @@ -1154,6 +1154,7 @@ dependencies = [ "gh-emoji 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "minify 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 454a366..64e0bd9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "snekdown" -version = "0.25.0" +version = "0.26.0" authors = ["trivernis "] edition = "2018" license-file = "LICENSE" @@ -39,3 +39,4 @@ mime_guess = "2.0.3" mime = "0.3.16" base64 = "0.12.3" rayon = "1.3.1" +maplit = "1.0.2" \ No newline at end of file diff --git a/src/elements/mod.rs b/src/elements/mod.rs index 1eba1d0..aee972d 100644 --- a/src/elements/mod.rs +++ b/src/elements/mod.rs @@ -1,7 +1,7 @@ pub mod tokens; use crate::format::PlaceholderTemplate; -use crate::references::configuration::{ConfigRefEntry, Configuration}; +use crate::references::configuration::{ConfigRefEntry, Configuration, Value}; use crate::references::placeholders::ProcessPlaceholders; use crate::references::templates::{Template, TemplateVariable}; use asciimath_rs::elements::special::Expression; @@ -10,6 +10,7 @@ use bibliographix::bibliography::bibliography_entry::BibliographyEntryReference; use bibliographix::references::bib_reference::BibRefAnchor; use std::collections::HashMap; use std::fs::read; +use std::iter::FromIterator; use std::path::PathBuf; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex, RwLock}; @@ -663,6 +664,19 @@ impl Metadata for InlineMetadata { } } +impl Into> for InlineMetadata { + fn into(self) -> HashMap { + HashMap::from_iter(self.data.iter().filter_map(|(k, v)| match v { + MetadataValue::String(s) => Some((k.clone(), Value::String(s.clone()))), + MetadataValue::Bool(b) => Some((k.clone(), Value::Bool(*b))), + MetadataValue::Integer(i) => Some((k.clone(), Value::Integer(*i))), + MetadataValue::Float(f) => Some((k.clone(), Value::Float(*f))), + MetadataValue::Template(t) => Some((k.clone(), Value::Template(t.clone()))), + _ => None, + })) + } +} + impl Image { pub fn get_content(&self) -> Option> { let path = PathBuf::from(&self.url.url); diff --git a/src/format/html/to_html.rs b/src/format/html/to_html.rs index 0b5381f..61d4028 100644 --- a/src/format/html/to_html.rs +++ b/src/format/html/to_html.rs @@ -1,6 +1,7 @@ use crate::elements::*; use crate::format::html::html_writer::HTMLWriter; use crate::format::PlaceholderTemplate; +use crate::references::configuration::keys::META_LANG; use crate::references::templates::{Template, TemplateVariable}; use asciimath_rs::format::mathml::ToMathML; use htmlescape::encode_attribute; @@ -101,11 +102,11 @@ impl ToHtml for Document { "".to_string() }; if self.is_root { - let language = if let Some(entry) = self.config.get_entry("lang") { - entry.get().as_string() - } else { - "en".to_string() - }; + let language = self + .config + .get_entry(META_LANG) + .map(|e| e.get().as_string()) + .unwrap_or("en".to_string()); let style = minify(std::include_str!("assets/style.css")); writer.write(" ParseResult; @@ -319,15 +320,19 @@ impl ParseBlock for Parser { self.section_return = Some(0); return Err(self.ctm.rewind_with_error(start_index)); } + let metadata = self + .parse_inline_metadata() + .ok() + .map(|m| m.into()) + .unwrap_or(HashMap::new()); self.ctm.seek_whitespace(); - match self.import(path.clone()) { + match self.import(path.clone(), metadata) { ImportType::Document(Ok(anchor)) => Ok(Some(Import { path, anchor })), - ImportType::Stylesheet(Ok(content)) => { - self.document.stylesheets.push(content); - Ok(None) - } + ImportType::Stylesheet(_) => Ok(None), + ImportType::Bibliography(_) => Ok(None), + ImportType::Manifest(_) => Ok(None), _ => Err(self.ctm.err()), } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index aa1ee83..de027da 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -4,12 +4,13 @@ pub(crate) mod line; use self::block::ParseBlock; use crate::elements::{Document, ImportAnchor}; -use crate::references::configuration::Configuration; +use crate::references::configuration::{Configuration, Value}; use bibliographix::bib_manager::BibManager; use charred::tapemachine::{CharTapeMachine, TapeError, TapeResult}; use colored::*; use crossbeam_utils::sync::WaitGroup; use regex::Regex; +use std::collections::HashMap; use std::fs::{read_to_string, File}; use std::io; use std::io::{BufRead, BufReader, Cursor}; @@ -20,6 +21,14 @@ use std::thread; pub type ParseResult = TapeResult; pub type ParseError = TapeError; +const DEFAULT_IMPORTS: &'static [(&str, &str)] = &[ + ("snekdown.toml", "manifest"), + ("manifest.toml", "manifest"), + ("bibliography.toml", "bibliography"), + ("bibliography.bib.toml", "bibliography"), + ("style.css", "stylesheet"), +]; + pub struct Parser { pub(crate) ctm: CharTapeMachine, section_nesting: u8, @@ -216,27 +225,59 @@ impl Parser { Ok(()) } + /// Returns the text of an imported text file + fn import_text_file(&self, path: PathBuf) -> ParseResult { + read_to_string(path).map_err(|_| self.ctm.err()) + } + + fn import_stylesheet(&mut self, path: PathBuf) -> ParseResult<()> { + let content = self.import_text_file(path)?; + self.document.stylesheets.push(content); + + Ok(()) + } + + fn import_manifest(&mut self, path: PathBuf) -> ParseResult<()> { + let contents = self.import_text_file(path)?; + let value = contents + .parse::() + .map_err(|_| self.ctm.err())?; + self.document.config.set_from_toml(&value); + + Ok(()) + } + /// Imports a path - fn import(&mut self, path: String) -> ImportType { + fn import(&mut self, path: String, args: HashMap) -> ImportType { let path = self.transform_path(path); - lazy_static::lazy_static! { - static ref BIB_NAME: Regex = Regex::new(r".*\.bib\.toml$").unwrap(); - } - - if let Some(fname) = path.file_name().and_then(|f| Some(f.to_str().unwrap())) { - if BIB_NAME.is_match(fname) { - return ImportType::Bibliography(self.import_bib(path)); + match args.get("type").map(|e| e.as_string().to_lowercase()) { + Some(s) if s == "stylesheet".to_string() => { + ImportType::Stylesheet(self.import_stylesheet(path)) } - } - match path.extension() { - Some(e) if e.to_str().unwrap().to_lowercase() == "css" => { - if let Ok(content) = read_to_string(path) { - ImportType::Stylesheet(Ok(content)) - } else { - ImportType::Stylesheet(Err(ParseError::new(self.ctm.get_index()))) + Some(s) if s == "document".to_string() => { + ImportType::Document(self.import_document(path)) + } + Some(s) if s == "bibliography".to_string() => { + ImportType::Bibliography(self.import_bib(path)) + } + Some(s) if s == "manifest".to_string() || s == "config" => { + ImportType::Manifest(self.import_manifest(path)) + } + _ => { + lazy_static::lazy_static! { + static ref BIB_NAME: Regex = Regex::new(r".*\.bib\.toml$").unwrap(); + } + if let Some(fname) = path.file_name().and_then(|f| Some(f.to_str().unwrap())) { + if BIB_NAME.is_match(fname) { + return ImportType::Bibliography(self.import_bib(path)); + } + } + match path.extension().map(|e| e.to_str().unwrap().to_lowercase()) { + Some(e) if e == "css" => ImportType::Stylesheet(self.import_stylesheet(path)), + Some(e) if e == "toml" => ImportType::Manifest(self.import_manifest(path)), + _ => ImportType::Document(self.import_document(path)), } } - _ => ImportType::Document(self.import_document(path)), } } @@ -263,6 +304,14 @@ impl Parser { let wg = self.wg.clone(); self.wg = WaitGroup::new(); + if !self.is_child { + for (path, file_type) in DEFAULT_IMPORTS { + self.import( + path.to_string(), + maplit::hashmap! {"type".to_string() => Value::String(file_type.to_string())}, + ); + } + } wg.wait(); self.document.post_process(); let document = self.document.clone(); @@ -274,6 +323,7 @@ impl Parser { pub(crate) enum ImportType { Document(ParseResult>>), - Stylesheet(ParseResult), + Stylesheet(ParseResult<()>), Bibliography(ParseResult<()>), + Manifest(ParseResult<()>), } diff --git a/src/references/configuration/config.rs b/src/references/configuration/config.rs deleted file mode 100644 index 31c992c..0000000 --- a/src/references/configuration/config.rs +++ /dev/null @@ -1,22 +0,0 @@ -use serde_derive::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct RootConfig { - pub(crate) bibliography: Option, - pub(crate) metadata: Option, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct BibConfig { - pub(crate) entry_display: Option, - pub(crate) reference_display: Option, - pub(crate) hide_unused: Option, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct MetaConfig { - pub(crate) author: Option, - pub(crate) date: Option, - pub(crate) title: Option, - pub(crate) language: Option, -} diff --git a/src/references/configuration/default.toml b/src/references/configuration/default.toml deleted file mode 100644 index f6257e6..0000000 --- a/src/references/configuration/default.toml +++ /dev/null @@ -1,4 +0,0 @@ -[bibliography] -entry_display = "{{number}}: {{author}} - {{title}} - {{date}} - {{url}}" -reference_display = "{{number}}" -hide_unused = true \ No newline at end of file diff --git a/src/references/configuration/keys.rs b/src/references/configuration/keys.rs index 314a592..37636b9 100644 --- a/src/references/configuration/keys.rs +++ b/src/references/configuration/keys.rs @@ -1,8 +1,2 @@ -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"; -pub const META_LANG: &str = "lang"; +pub const META_LANG: &str = "language"; diff --git a/src/references/configuration/mod.rs b/src/references/configuration/mod.rs index 05920ad..d7dfa1f 100644 --- a/src/references/configuration/mod.rs +++ b/src/references/configuration/mod.rs @@ -1,13 +1,9 @@ use crate::elements::MetadataValue; -use crate::references::configuration::config::RootConfig; -use crate::references::configuration::keys::{ - BIB_DISPLAY, BIB_HIDE_UNUSED, BIB_REF_DISPLAY, META_AUTHOR, META_DATE, META_LANG, META_TITLE, -}; +use crate::references::configuration::keys::{BIB_REF_DISPLAY, META_LANG}; use crate::references::templates::Template; use std::collections::HashMap; use std::sync::{Arc, RwLock}; -pub mod config; pub(crate) mod keys; #[derive(Clone, Debug)] @@ -57,46 +53,20 @@ impl ConfigEntry { } } -impl Configuration { - pub fn new() -> Self { - Self { - config: Arc::new(RwLock::new(HashMap::new())), - } - } - - pub fn default() -> Self { +impl Default for Configuration { + 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.set(BIB_REF_DISPLAY, Value::String("number".to_string())); + self_config.set(META_LANG, Value::String("en".to_string())); 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())) - } - if let Some(lang) = &meta.language { - self.set(META_LANG, Value::String(lang.clone())) - } +impl Configuration { + pub fn new() -> Self { + Self { + config: Arc::new(RwLock::new(HashMap::new())), } } @@ -145,4 +115,21 @@ impl Configuration { _ => {} } } + + pub fn set_from_toml(&mut self, value: &toml::Value) -> Option<()> { + let table = value.as_table()?; + 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())), + _ => {} + }; + }); + + Some(()) + } }