Add imports in metadata files and update README

Signed-off-by: trivernis <trivernis@protonmail.com>
feature/epub-rendering
trivernis 4 years ago
parent 609bc18d7f
commit 932c213ff2
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

2
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.26.1" version = "0.26.2"
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)",

@ -1,6 +1,6 @@
[package] [package]
name = "snekdown" name = "snekdown"
version = "0.26.1" version = "0.26.2"
authors = ["trivernis <trivernis@protonmail.com>"] authors = ["trivernis <trivernis@protonmail.com>"]
edition = "2018" edition = "2018"
license-file = "LICENSE" license-file = "LICENSE"

@ -72,9 +72,17 @@ Imports are parsed via multithreading.
<[document.md] <[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 ### Tables
@ -133,6 +141,26 @@ Placeholder
[key = [[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 #### 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: Entries can also be defined in a separate toml file with the following data layout:
```toml ```toml
# snekdown.toml
[BIB_KEY] [BIB_KEY]
key = "value" key = "value"
@ -233,7 +262,7 @@ The end goal is to have a markup language with features similar to LaTeX.
- [x] Emojis (\:emoji:) - [x] Emojis (\:emoji:)
- [x] Colors - [x] Colors
- [x] Watching and rendering on change - [x] Watching and rendering on change
- [ ] Metadata files - [x] Metadata files
- [x] Bibliography - [x] Bibliography
- [x] Math - [x] Math
- [ ] Text sizes - [ ] Text sizes

@ -328,7 +328,7 @@ impl ParseBlock for Parser {
self.ctm.seek_whitespace(); 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::Document(Ok(anchor)) => Ok(Some(Import { path, anchor })),
ImportType::Stylesheet(_) => Ok(None), ImportType::Stylesheet(_) => Ok(None),
ImportType::Bibliography(_) => Ok(None), ImportType::Bibliography(_) => Ok(None),

@ -4,6 +4,9 @@ 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::keys::{
IMP_BIBLIOGRAPHY, IMP_CONFIGS, IMP_IGNORE, IMP_STYLESHEETS,
};
use crate::references::configuration::{Configuration, Value}; 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};
@ -25,7 +28,7 @@ const DEFAULT_IMPORTS: &'static [(&str, &str)] = &[
("snekdown.toml", "manifest"), ("snekdown.toml", "manifest"),
("manifest.toml", "manifest"), ("manifest.toml", "manifest"),
("bibliography.toml", "bibliography"), ("bibliography.toml", "bibliography"),
("bibliography.bib.toml", "bibliography"), ("bibliography2.bib.toml", "bibliography"),
("style.css", "stylesheet"), ("style.css", "stylesheet"),
]; ];
@ -248,8 +251,27 @@ impl Parser {
} }
/// Imports a path /// Imports a path
fn import(&mut self, path: String, args: HashMap<String, Value>) -> ImportType { fn import(&mut self, path: String, args: &HashMap<String, Value>) -> ImportType {
let path = self.transform_path(path); 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::<Vec<String>>();
if ignore.contains(&fname) {
return ImportType::None;
}
}
}
match args.get("type").map(|e| e.as_string().to_lowercase()) { match args.get("type").map(|e| e.as_string().to_lowercase()) {
Some(s) if s == "stylesheet".to_string() => { Some(s) if s == "stylesheet".to_string() => {
ImportType::Stylesheet(self.import_stylesheet(path)) ImportType::Stylesheet(self.import_stylesheet(path))
@ -308,17 +330,59 @@ impl Parser {
for (path, file_type) in DEFAULT_IMPORTS { for (path, file_type) in DEFAULT_IMPORTS {
self.import( self.import(
path.to_string(), 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(); wg.wait();
if !self.is_child {
self.import_from_config();
}
self.document.post_process(); self.document.post_process();
let document = self.document.clone(); let document = self.document.clone();
self.document = Document::new(!self.is_child); self.document = Document::new(!self.is_child);
document 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 { pub(crate) enum ImportType {
@ -326,4 +390,5 @@ pub(crate) enum ImportType {
Stylesheet(ParseResult<()>), Stylesheet(ParseResult<()>),
Bibliography(ParseResult<()>), Bibliography(ParseResult<()>),
Manifest(ParseResult<()>), Manifest(ParseResult<()>),
None,
} }

@ -1,2 +1,7 @@
pub const BIB_REF_DISPLAY: &str = "bib-ref-display"; pub const BIB_REF_DISPLAY: &str = "bib-ref-display";
pub const META_LANG: &str = "language"; 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";

@ -1,6 +1,7 @@
use crate::elements::MetadataValue; use crate::elements::MetadataValue;
use crate::references::configuration::keys::{BIB_REF_DISPLAY, META_LANG}; use crate::references::configuration::keys::{BIB_REF_DISPLAY, META_LANG};
use crate::references::templates::Template; use crate::references::templates::Template;
use serde::export::TryFrom;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
@ -13,6 +14,7 @@ pub enum Value {
Float(f64), Float(f64),
Integer(i64), Integer(i64),
Template(Template), Template(Template),
Array(Vec<Value>),
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -34,6 +36,9 @@ impl Value {
Value::Integer(int) => format!("{}", int), Value::Integer(int) => format!("{}", int),
Value::Float(f) => format!("{:02}", f), Value::Float(f) => format!("{:02}", f),
Value::Bool(b) => format!("{}", b), Value::Bool(b) => format!("{}", b),
Value::Array(a) => a.iter().fold("".to_string(), |a, b| {
format!("{} \"{}\"", a, b.as_string())
}),
_ => "".to_string(), _ => "".to_string(),
} }
} }
@ -117,19 +122,35 @@ impl Configuration {
} }
pub fn set_from_toml(&mut self, value: &toml::Value) -> Option<()> { 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)| { table.iter().for_each(|(k, v)| {
match v { match v {
toml::Value::Table(_) => self.set_from_toml(v).unwrap_or(()), toml::Value::Table(_) => self.set_from_toml(v).unwrap_or(()),
toml::Value::Float(f) => self.set(k, Value::Float(*f)), _ => self.set(k, Value::try_from(v.clone()).unwrap()),
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(()) Some(())
} }
} }
impl TryFrom<toml::Value> for Value {
type Error = ();
fn try_from(value: toml::Value) -> Result<Self, Self::Error> {
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::<Vec<Value>>(),
)),
}
}
}

Loading…
Cancel
Save