Add metadata file imports and import types

Signed-off-by: trivernis <trivernis@protonmail.com>
feature/epub-rendering
trivernis 4 years ago
parent 09bbabfdc2
commit f5bf361795
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

3
Cargo.lock generated

@ -1142,7 +1142,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "snekdown" name = "snekdown"
version = "0.25.0" version = "0.26.0"
dependencies = [ dependencies = [
"asciimath-rs 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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)", "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)", "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 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)", "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)", "minify 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",

@ -1,6 +1,6 @@
[package] [package]
name = "snekdown" name = "snekdown"
version = "0.25.0" version = "0.26.0"
authors = ["trivernis <trivernis@protonmail.com>"] authors = ["trivernis <trivernis@protonmail.com>"]
edition = "2018" edition = "2018"
license-file = "LICENSE" license-file = "LICENSE"
@ -39,3 +39,4 @@ mime_guess = "2.0.3"
mime = "0.3.16" mime = "0.3.16"
base64 = "0.12.3" base64 = "0.12.3"
rayon = "1.3.1" rayon = "1.3.1"
maplit = "1.0.2"

@ -1,7 +1,7 @@
pub mod tokens; pub mod tokens;
use crate::format::PlaceholderTemplate; 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::placeholders::ProcessPlaceholders;
use crate::references::templates::{Template, TemplateVariable}; use crate::references::templates::{Template, TemplateVariable};
use asciimath_rs::elements::special::Expression; use asciimath_rs::elements::special::Expression;
@ -10,6 +10,7 @@ use bibliographix::bibliography::bibliography_entry::BibliographyEntryReference;
use bibliographix::references::bib_reference::BibRefAnchor; use bibliographix::references::bib_reference::BibRefAnchor;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::read; use std::fs::read;
use std::iter::FromIterator;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, RwLock}; use std::sync::{Arc, Mutex, RwLock};
@ -663,6 +664,19 @@ impl Metadata for InlineMetadata {
} }
} }
impl Into<HashMap<String, Value>> for InlineMetadata {
fn into(self) -> HashMap<String, Value> {
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 { impl Image {
pub fn get_content(&self) -> Option<Vec<u8>> { pub fn get_content(&self) -> Option<Vec<u8>> {
let path = PathBuf::from(&self.url.url); let path = PathBuf::from(&self.url.url);

@ -1,6 +1,7 @@
use crate::elements::*; use crate::elements::*;
use crate::format::html::html_writer::HTMLWriter; use crate::format::html::html_writer::HTMLWriter;
use crate::format::PlaceholderTemplate; use crate::format::PlaceholderTemplate;
use crate::references::configuration::keys::META_LANG;
use crate::references::templates::{Template, TemplateVariable}; use crate::references::templates::{Template, TemplateVariable};
use asciimath_rs::format::mathml::ToMathML; use asciimath_rs::format::mathml::ToMathML;
use htmlescape::encode_attribute; use htmlescape::encode_attribute;
@ -101,11 +102,11 @@ impl ToHtml for Document {
"".to_string() "".to_string()
}; };
if self.is_root { if self.is_root {
let language = if let Some(entry) = self.config.get_entry("lang") { let language = self
entry.get().as_string() .config
} else { .get_entry(META_LANG)
"en".to_string() .map(|e| e.get().as_string())
}; .unwrap_or("en".to_string());
let style = minify(std::include_str!("assets/style.css")); let style = minify(std::include_str!("assets/style.css"));
writer.write("<!DOCTYPE html".to_string())?; writer.write("<!DOCTYPE html".to_string())?;
writer.write("<html lang=\"".to_string())?; writer.write("<html lang=\"".to_string())?;

@ -7,6 +7,7 @@ use crate::parser::inline::ParseInline;
use crate::parser::line::ParseLine; use crate::parser::line::ParseLine;
use crate::parser::ImportType; use crate::parser::ImportType;
use crate::Parser; use crate::Parser;
use std::collections::HashMap;
pub(crate) trait ParseBlock { pub(crate) trait ParseBlock {
fn parse_block(&mut self) -> ParseResult<Block>; fn parse_block(&mut self) -> ParseResult<Block>;
@ -319,15 +320,19 @@ impl ParseBlock for Parser {
self.section_return = Some(0); self.section_return = Some(0);
return Err(self.ctm.rewind_with_error(start_index)); 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(); 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::Document(Ok(anchor)) => Ok(Some(Import { path, anchor })),
ImportType::Stylesheet(Ok(content)) => { ImportType::Stylesheet(_) => Ok(None),
self.document.stylesheets.push(content); ImportType::Bibliography(_) => Ok(None),
Ok(None) ImportType::Manifest(_) => Ok(None),
}
_ => Err(self.ctm.err()), _ => Err(self.ctm.err()),
} }
} }

@ -4,12 +4,13 @@ pub(crate) mod line;
use self::block::ParseBlock; use self::block::ParseBlock;
use crate::elements::{Document, ImportAnchor}; use crate::elements::{Document, ImportAnchor};
use crate::references::configuration::Configuration; use crate::references::configuration::{Configuration, Value};
use bibliographix::bib_manager::BibManager; use bibliographix::bib_manager::BibManager;
use charred::tapemachine::{CharTapeMachine, TapeError, TapeResult}; use charred::tapemachine::{CharTapeMachine, TapeError, TapeResult};
use colored::*; use colored::*;
use crossbeam_utils::sync::WaitGroup; use crossbeam_utils::sync::WaitGroup;
use regex::Regex; use regex::Regex;
use std::collections::HashMap;
use std::fs::{read_to_string, File}; use std::fs::{read_to_string, File};
use std::io; use std::io;
use std::io::{BufRead, BufReader, Cursor}; use std::io::{BufRead, BufReader, Cursor};
@ -20,6 +21,14 @@ use std::thread;
pub type ParseResult<T> = TapeResult<T>; pub type ParseResult<T> = TapeResult<T>;
pub type ParseError = TapeError; 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 struct Parser {
pub(crate) ctm: CharTapeMachine, pub(crate) ctm: CharTapeMachine,
section_nesting: u8, section_nesting: u8,
@ -216,27 +225,59 @@ impl Parser {
Ok(()) Ok(())
} }
/// Returns the text of an imported text file
fn import_text_file(&self, path: PathBuf) -> ParseResult<String> {
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::<toml::Value>()
.map_err(|_| self.ctm.err())?;
self.document.config.set_from_toml(&value);
Ok(())
}
/// Imports a path /// Imports a path
fn import(&mut self, path: String) -> ImportType { fn import(&mut self, path: String, args: HashMap<String, Value>) -> ImportType {
let path = self.transform_path(path); let path = self.transform_path(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))
}
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! { lazy_static::lazy_static! {
static ref BIB_NAME: Regex = Regex::new(r".*\.bib\.toml$").unwrap(); 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 let Some(fname) = path.file_name().and_then(|f| Some(f.to_str().unwrap())) {
if BIB_NAME.is_match(fname) { if BIB_NAME.is_match(fname) {
return ImportType::Bibliography(self.import_bib(path)); return ImportType::Bibliography(self.import_bib(path));
} }
} }
match path.extension() { match path.extension().map(|e| e.to_str().unwrap().to_lowercase()) {
Some(e) if e.to_str().unwrap().to_lowercase() == "css" => { Some(e) if e == "css" => ImportType::Stylesheet(self.import_stylesheet(path)),
if let Ok(content) = read_to_string(path) { Some(e) if e == "toml" => ImportType::Manifest(self.import_manifest(path)),
ImportType::Stylesheet(Ok(content)) _ => ImportType::Document(self.import_document(path)),
} else {
ImportType::Stylesheet(Err(ParseError::new(self.ctm.get_index())))
} }
} }
_ => ImportType::Document(self.import_document(path)),
} }
} }
@ -263,6 +304,14 @@ impl Parser {
let wg = self.wg.clone(); let wg = self.wg.clone();
self.wg = WaitGroup::new(); 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(); wg.wait();
self.document.post_process(); self.document.post_process();
let document = self.document.clone(); let document = self.document.clone();
@ -274,6 +323,7 @@ impl Parser {
pub(crate) enum ImportType { pub(crate) enum ImportType {
Document(ParseResult<Arc<RwLock<ImportAnchor>>>), Document(ParseResult<Arc<RwLock<ImportAnchor>>>),
Stylesheet(ParseResult<String>), Stylesheet(ParseResult<()>),
Bibliography(ParseResult<()>), Bibliography(ParseResult<()>),
Manifest(ParseResult<()>),
} }

@ -1,22 +0,0 @@
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>,
pub(crate) language: Option<String>,
}

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

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

@ -1,13 +1,9 @@
use crate::elements::MetadataValue; use crate::elements::MetadataValue;
use crate::references::configuration::config::RootConfig; use crate::references::configuration::keys::{BIB_REF_DISPLAY, META_LANG};
use crate::references::configuration::keys::{
BIB_DISPLAY, BIB_HIDE_UNUSED, BIB_REF_DISPLAY, META_AUTHOR, META_DATE, META_LANG, META_TITLE,
};
use crate::references::templates::Template; use crate::references::templates::Template;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
pub mod config;
pub(crate) mod keys; pub(crate) mod keys;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -57,46 +53,20 @@ impl ConfigEntry {
} }
} }
impl Configuration { impl Default for Configuration {
pub fn new() -> Self { fn default() -> Self {
Self {
config: Arc::new(RwLock::new(HashMap::new())),
}
}
pub fn default() -> Self {
let mut self_config = Self::new(); 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.set(BIB_REF_DISPLAY, Value::String("number".to_string()));
self_config.assign_config(&CONFIG); self_config.set(META_LANG, Value::String("en".to_string()));
self_config self_config
} }
}
pub fn assign_config(&mut self, config: &RootConfig) { impl Configuration {
if let Some(bib) = &config.bibliography { pub fn new() -> Self {
if let Some(cfg) = &bib.entry_display { Self {
self.set(BIB_DISPLAY, Value::String(cfg.clone())) config: Arc::new(RwLock::new(HashMap::new())),
}
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()))
}
} }
} }
@ -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(())
}
} }

Loading…
Cancel
Save