summaryrefslogtreecommitdiff
path: root/src/metadata.rs
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2020-08-08 18:43:57 +0300
committerLars Wirzenius <liw@liw.fi>2020-08-08 20:47:22 +0300
commitd1651321ec1cd1e02fb93fe6e31ab4de115356c9 (patch)
treef5966966f0cb910659a62afa63c7f089913e9937 /src/metadata.rs
parentc3e8c88f3294338e8ea4678fb5493c96150a4e3c (diff)
downloadsubplot-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.rs277
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(())
+}