diff options
author | Lars Wirzenius <liw@liw.fi> | 2020-08-08 18:43:57 +0300 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2020-08-08 20:47:22 +0300 |
commit | d1651321ec1cd1e02fb93fe6e31ab4de115356c9 (patch) | |
tree | f5966966f0cb910659a62afa63c7f089913e9937 /src/metadata.rs | |
parent | c3e8c88f3294338e8ea4678fb5493c96150a4e3c (diff) | |
download | subplot-d1651321ec1cd1e02fb93fe6e31ab4de115356c9.tar.gz |
refactor: split stuff from src/ast.rs into smaller modules
This only moves things around, to avoid huge source code modules. It
doesn't rename functions, add unit tests, or similar.
* src/datafiles.rs: DataFile, DataFiles
* src/metata.rs: Metadata
* src/panhelper.rs: functions for querying Pandoc Attrs
* src/policy.rs: the get_basedir_from function; place for later policy
functions
* src/typeset.rs: functions to produce Pandoc AST nodes
* srv/visitor/*: various MutVisitor implementations for traversing
ASTs, and their helper functions
Diffstat (limited to 'src/metadata.rs')
-rw-r--r-- | src/metadata.rs | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/src/metadata.rs b/src/metadata.rs new file mode 100644 index 0000000..05f686b --- /dev/null +++ b/src/metadata.rs @@ -0,0 +1,277 @@ +use crate::Bindings; +use crate::Result; + +use std::ops::Deref; +use std::path::{Path, PathBuf}; + +use pandoc_ast::{Inline, Map, MetaValue, Pandoc}; + +/// Metadata of a document, as needed by Subplot. +#[derive(Debug)] +pub struct Metadata { + title: String, + date: Option<String>, + bindings_filenames: Vec<PathBuf>, + bindings: Bindings, + functions_filenames: Vec<PathBuf>, + template: Option<String>, + bibliographies: Vec<PathBuf>, + /// Extra class names which should be considered 'correct' for this document + classes: Vec<String>, +} + +impl Metadata { + /// Construct a Metadata from a Document, if possible. + pub fn new<P>(basedir: P, doc: &Pandoc) -> Result<Metadata> + where + P: AsRef<Path>, + { + let title = get_title(&doc.meta)?; + let date = get_date(&doc.meta); + let bindings_filenames = get_bindings_filenames(basedir.as_ref(), &doc.meta); + let functions_filenames = get_functions_filenames(basedir.as_ref(), &doc.meta); + let template = get_template_name(&doc.meta)?; + let mut bindings = Bindings::new(); + + let bibliographies = get_bibliographies(basedir.as_ref(), &doc.meta); + let classes = get_classes(&doc.meta); + + get_bindings(&bindings_filenames, &mut bindings)?; + Ok(Metadata { + title, + date, + bindings_filenames, + bindings, + functions_filenames, + template, + bibliographies, + classes, + }) + } + + /// Return title of document. + pub fn title(&self) -> &str { + &self.title + } + + /// Return date of document, if any. + pub fn date(&self) -> Option<&str> { + if let Some(date) = &self.date { + Some(&date) + } else { + None + } + } + + /// Return filename where bindings are specified. + pub fn bindings_filenames(&self) -> Vec<&Path> { + self.bindings_filenames.iter().map(|f| f.as_ref()).collect() + } + + /// Return filename where functions are specified. + pub fn functions_filenames(&self) -> Vec<&Path> { + self.functions_filenames + .iter() + .map(|f| f.as_ref()) + .collect() + } + + /// Return the name of the code template, if specified. + pub fn template_name(&self) -> Option<&str> { + match &self.template { + Some(x) => Some(&x), + None => None, + } + } + + /// Return the bindings. + pub fn bindings(&self) -> &Bindings { + &self.bindings + } + + /// Return the bibliographies. + pub fn bibliographies(&self) -> Vec<&Path> { + self.bibliographies.iter().map(|x| x.as_path()).collect() + } + + /// The classes which this document also claims are valid + pub fn classes(&self) -> impl Iterator<Item = &str> { + self.classes.iter().map(Deref::deref) + } +} + +type Mapp = Map<String, MetaValue>; + +fn get_title(map: &Mapp) -> Result<String> { + if let Some(s) = get_string(map, "title") { + Ok(s) + } else { + Ok("".to_string()) + } +} + +fn get_date(map: &Mapp) -> Option<String> { + if let Some(s) = get_string(map, "date") { + Some(s) + } else { + None + } +} + +fn get_bindings_filenames<P>(basedir: P, map: &Mapp) -> Vec<PathBuf> +where + P: AsRef<Path>, +{ + get_paths(basedir, map, "bindings") +} + +fn get_functions_filenames<P>(basedir: P, map: &Mapp) -> Vec<PathBuf> +where + P: AsRef<Path>, +{ + get_paths(basedir, map, "functions") +} + +fn get_template_name(map: &Mapp) -> Result<Option<String>> { + match get_string(map, "template") { + Some(s) => Ok(Some(s)), + None => Ok(None), + } +} + +fn get_paths<P>(basedir: P, map: &Mapp, field: &str) -> Vec<PathBuf> +where + P: AsRef<Path>, +{ + match map.get(field) { + None => vec![], + Some(v) => pathbufs(basedir, v), + } +} + +fn get_string(map: &Mapp, field: &str) -> Option<String> { + let v = match map.get(field) { + None => return None, + Some(s) => s, + }; + let v = match v { + pandoc_ast::MetaValue::MetaString(s) => s.to_string(), + pandoc_ast::MetaValue::MetaInlines(vec) => join(&vec), + _ => panic!("don't know how to handle: {:?}", v), + }; + Some(v) +} + +fn get_bibliographies<P>(basedir: P, map: &Mapp) -> Vec<PathBuf> +where + P: AsRef<Path>, +{ + let v = match map.get("bibliography") { + None => return vec![], + Some(s) => s, + }; + pathbufs(basedir, v) +} + +fn pathbufs<P>(basedir: P, v: &MetaValue) -> Vec<PathBuf> +where + P: AsRef<Path>, +{ + let mut bufs = vec![]; + push_pathbufs(basedir, v, &mut bufs); + bufs +} + +fn get_classes(map: &Mapp) -> Vec<String> { + let mut ret = Vec::new(); + if let Some(classes) = map.get("classes") { + push_strings(classes, &mut ret); + } + ret +} + +fn push_strings(v: &MetaValue, strings: &mut Vec<String>) { + match v { + MetaValue::MetaString(s) => strings.push(s.to_string()), + MetaValue::MetaInlines(vec) => strings.push(join(&vec)), + MetaValue::MetaList(values) => { + for value in values { + push_strings(value, strings); + } + } + _ => panic!("don't know how to handle: {:?}", v), + }; +} + +fn push_pathbufs<P>(basedir: P, v: &MetaValue, bufs: &mut Vec<PathBuf>) +where + P: AsRef<Path>, +{ + match v { + MetaValue::MetaString(s) => bufs.push(basedir.as_ref().join(Path::new(s))), + MetaValue::MetaInlines(vec) => bufs.push(basedir.as_ref().join(Path::new(&join(&vec)))), + MetaValue::MetaList(values) => { + for value in values { + push_pathbufs(basedir.as_ref(), value, bufs); + } + } + _ => panic!("don't know how to handle: {:?}", v), + }; +} + +fn join(vec: &[Inline]) -> String { + let mut buf = String::new(); + join_into_buffer(vec, &mut buf); + buf +} + +fn join_into_buffer(vec: &[Inline], buf: &mut String) { + for item in vec { + match item { + pandoc_ast::Inline::Str(s) => buf.push_str(&s), + pandoc_ast::Inline::Emph(v) => join_into_buffer(v, buf), + pandoc_ast::Inline::Strong(v) => join_into_buffer(v, buf), + pandoc_ast::Inline::Strikeout(v) => join_into_buffer(v, buf), + pandoc_ast::Inline::Superscript(v) => join_into_buffer(v, buf), + pandoc_ast::Inline::Subscript(v) => join_into_buffer(v, buf), + pandoc_ast::Inline::SmallCaps(v) => join_into_buffer(v, buf), + pandoc_ast::Inline::Space => buf.push_str(" "), + pandoc_ast::Inline::SoftBreak => buf.push_str(" "), + pandoc_ast::Inline::LineBreak => buf.push_str(" "), + _ => panic!("unknown pandoc_ast::Inline component {:?}", item), + } + } +} + +#[cfg(test)] +mod test_join { + use super::join; + use pandoc_ast::Inline; + + #[test] + fn join_all_kinds() { + let v = vec![ + Inline::Str("a".to_string()), + Inline::Emph(vec![Inline::Str("b".to_string())]), + Inline::Strong(vec![Inline::Str("c".to_string())]), + Inline::Strikeout(vec![Inline::Str("d".to_string())]), + Inline::Superscript(vec![Inline::Str("e".to_string())]), + Inline::Subscript(vec![Inline::Str("f".to_string())]), + Inline::SmallCaps(vec![Inline::Str("g".to_string())]), + Inline::Space, + Inline::SoftBreak, + Inline::LineBreak, + ]; + assert_eq!(join(&v), "abcdefg "); + } +} + +fn get_bindings<P>(filenames: &[P], bindings: &mut Bindings) -> Result<()> +where + P: AsRef<Path>, +{ + for filename in filenames { + bindings.add_from_file(filename)?; + } + Ok(()) +} |