summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@digital-scurf.org>2021-11-12 10:59:10 +0000
committerDaniel Silverstone <dsilvers@digital-scurf.org>2021-11-19 20:19:00 +0000
commit03f4bff558807c3bf6b6ebd65aa1e547b9286461 (patch)
tree59c8b2ed142f7625916f2610713727f1537d03f2 /src
parent73e7188b2397d1afb5c550cf8863157c9174a4e5 (diff)
downloadsubplot-03f4bff558807c3bf6b6ebd65aa1e547b9286461.tar.gz
subplot: Rework for impls not template/functions
As the next step in polyglot documents, this reworks the internals to expect the metadata of documents to contain an impls mapping from template name to function filenames for that template. Sadly this does mean that if there're no function files, the document author will have to still specify an empty list, but that seems acceptable. Signed-off-by: Daniel Silverstone <dsilvers@digital-scurf.org>
Diffstat (limited to 'src')
-rw-r--r--src/ast.rs32
-rw-r--r--src/bin/subplot-filter.rs2
-rw-r--r--src/bin/subplot.rs8
-rw-r--r--src/doc.rs34
-rw-r--r--src/metadata.rs74
5 files changed, 80 insertions, 70 deletions
diff --git a/src/ast.rs b/src/ast.rs
index a901387..e6cdd66 100644
--- a/src/ast.rs
+++ b/src/ast.rs
@@ -3,6 +3,7 @@ use pandoc_ast::{Attr, Block, Inline, Map, MetaValue, Pandoc};
use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag};
use regex::Regex;
use serde::Deserialize;
+use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use tracing::{event, span, Level};
@@ -283,11 +284,11 @@ struct Metadata {
author: Option<String>,
date: Option<String>,
classes: Option<Vec<String>>,
- template: Option<String>,
bibliography: Option<Vec<PathBuf>>,
bindings: Option<Vec<PathBuf>>,
- functions: Option<Vec<PathBuf>>,
documentclass: Option<String>,
+ #[serde(default)]
+ impls: BTreeMap<String, Vec<PathBuf>>,
}
impl Metadata {
@@ -313,8 +314,13 @@ impl Metadata {
if let Some(v) = &self.classes {
map.insert(s("classes"), meta_strings(v));
}
- if let Some(v) = &self.template {
- map.insert(s("template"), meta_string(v));
+ if !self.impls.is_empty() {
+ let impls = self
+ .impls
+ .iter()
+ .map(|(k, v)| (k.to_owned(), Box::new(meta_path_bufs(v))))
+ .collect();
+ map.insert(s("impls"), MetaValue::MetaMap(impls));
}
if let Some(v) = &self.bibliography {
map.insert(s("bibliographies"), meta_path_bufs(v));
@@ -322,9 +328,6 @@ impl Metadata {
if let Some(v) = &self.bindings {
map.insert(s("bindings"), meta_path_bufs(v));
}
- if let Some(v) = &self.functions {
- map.insert(s("functions"), meta_path_bufs(v));
- }
if let Some(v) = &self.documentclass {
map.insert(s("documentclass"), meta_string(v));
}
@@ -440,23 +443,22 @@ mod test {
title: Foo Bar
date: today
classes: [json, text]
-template: rust
+impls:
+ python:
+ - foo.py
+ - bar.py
bibliography:
- foo.bib
- bar.bib
bindings:
- foo.yaml
- bar.yaml
-functions:
-- foo.py
-- bar.py
",
)
.unwrap();
assert_eq!(meta.title, "Foo Bar");
assert_eq!(meta.date.unwrap(), "today");
assert_eq!(meta.classes.unwrap(), &["json", "text"]);
- assert_eq!(meta.template.unwrap(), "rust");
assert_eq!(
meta.bibliography.unwrap(),
&[path("foo.bib"), path("bar.bib")]
@@ -465,7 +467,11 @@ functions:
meta.bindings.unwrap(),
&[path("foo.yaml"), path("bar.yaml")]
);
- assert_eq!(meta.functions.unwrap(), &[path("foo.py"), path("bar.py")]);
+ assert!(!meta.impls.is_empty());
+ for (k, v) in meta.impls.iter() {
+ assert_eq!(k, "python");
+ assert_eq!(v, &[path("foo.py"), path("bar.py")]);
+ }
}
fn path(s: &str) -> PathBuf {
diff --git a/src/bin/subplot-filter.rs b/src/bin/subplot-filter.rs
index 08f7261..6c01241 100644
--- a/src/bin/subplot-filter.rs
+++ b/src/bin/subplot-filter.rs
@@ -8,7 +8,7 @@ fn main() -> Result<()> {
stdin.read_to_string(&mut buffer)?;
let basedir = std::path::Path::new(".");
let style = Style::default();
- let mut doc = Document::from_json(&basedir, vec![], &buffer, style)?;
+ let mut doc = Document::from_json(&basedir, vec![], &buffer, style, None)?;
doc.typeset();
let bytes = doc.ast()?.into_bytes();
io::stdout().write_all(&bytes)?;
diff --git a/src/bin/subplot.rs b/src/bin/subplot.rs
index 08c4713..25b73c2 100644
--- a/src/bin/subplot.rs
+++ b/src/bin/subplot.rs
@@ -160,7 +160,7 @@ impl Extract {
fn run(&self) -> Result<()> {
let span = span!(Level::TRACE, "extract");
let _enter = span.enter();
- let doc = load_document(&self.filename, Style::default())?;
+ let doc = load_document(&self.filename, Style::default(), None)?;
let files: Vec<&DataFile> = if self.embedded.is_empty() {
doc.files()
@@ -230,7 +230,7 @@ impl Filter {
Path::new(".")
};
let style = Style::default();
- let mut doc = Document::from_json(basedir, vec![], &buffer, style)?;
+ let mut doc = Document::from_json(basedir, vec![], &buffer, style, None)?;
doc.typeset();
let bytes = doc.ast()?.into_bytes();
if let Some(filename) = &self.output {
@@ -266,7 +266,7 @@ impl Metadata {
fn run(&self) -> Result<()> {
let span = span!(Level::TRACE, "metadata");
let _enter = span.enter();
- let mut doc = load_document(&self.filename, Style::default())?;
+ let mut doc = load_document(&self.filename, Style::default(), None)?;
let meta = cli::Metadata::try_from(&mut doc)?;
match self.output_format {
cli::OutputFormat::Plain => meta.write_out(),
@@ -314,7 +314,7 @@ impl Docgen {
event!(Level::TRACE, "PDF output chosen");
style.typeset_links_as_notes();
}
- let mut doc = load_document(&self.input, style)?;
+ let mut doc = load_document(&self.input, style, None)?;
event!(Level::TRACE, "Got doc, now linting it");
doc.lint()?;
event!(Level::TRACE, "Doc linted ok");
diff --git a/src/doc.rs b/src/doc.rs
index 25dbdb3..a32d6da 100644
--- a/src/doc.rs
+++ b/src/doc.rs
@@ -73,7 +73,7 @@ static KNOWN_PANDOC_CLASSES: &[&str] = &["numberLines", "noNumberLines"];
/// use subplot;
/// let basedir = std::path::Path::new(".");
/// let style = subplot::Style::default();
-/// let doc = subplot::Document::from_file(&basedir, filename, style).unwrap();
+/// let doc = subplot::Document::from_file(&basedir, filename, style, None).unwrap();
/// assert_eq!(doc.files(), &[]);
/// ~~~~
#[derive(Debug)]
@@ -108,11 +108,12 @@ impl<'a> Document {
markdowns: Vec<PathBuf>,
mut ast: Pandoc,
style: Style,
+ template: Option<&str>,
) -> Result<Document>
where
P: AsRef<Path> + Debug,
{
- let meta = Metadata::new(basedir, &ast)?;
+ let meta = Metadata::new(basedir, &ast, template)?;
let mut linter = LintingVisitor::default();
event!(Level::TRACE, "Walking AST for linting...");
linter.walk_pandoc(&mut ast);
@@ -133,13 +134,14 @@ impl<'a> Document {
markdowns: Vec<PathBuf>,
json: &str,
style: Style,
+ template: Option<&str>,
) -> Result<Document>
where
P: AsRef<Path> + Debug,
{
event!(Level::TRACE, "Parsing document...");
let ast: Pandoc = serde_json::from_str(json)?;
- Self::from_ast(basedir, markdowns, ast, style)
+ Self::from_ast(basedir, markdowns, ast, style, template)
}
/// Construct a Document from a named file.
@@ -148,7 +150,12 @@ impl<'a> Document {
/// Pandoc to parse the file into an AST, so it can be a little
/// slow.
#[instrument(level = "trace")]
- pub fn from_file(basedir: &Path, filename: &Path, style: Style) -> Result<Document> {
+ pub fn from_file(
+ basedir: &Path,
+ filename: &Path,
+ style: Style,
+ template: Option<&str>,
+ ) -> Result<Document> {
let markdowns = vec![filename.to_path_buf()];
let mut pandoc = pandoc::new();
@@ -168,7 +175,7 @@ impl<'a> Document {
pandoc::PandocOutput::ToBuffer(o) => o,
_ => return Err(SubplotError::NotJson),
};
- let doc = Document::from_json(basedir, markdowns, &output, style)?;
+ let doc = Document::from_json(basedir, markdowns, &output, style, template)?;
event!(Level::TRACE, "Loaded document OK");
Ok(doc)
}
@@ -183,6 +190,7 @@ impl<'a> Document {
basedir: &Path,
filename: &Path,
style: Style,
+ template: Option<&str>,
) -> Result<Document> {
event!(
Level::TRACE,
@@ -194,7 +202,7 @@ impl<'a> Document {
let ast = ast::AbstractSyntaxTree::from_str(&markdown)?;
event!(Level::TRACE, "Parsed document OK");
- Self::from_ast(basedir, vec![filename], ast.to_pandoc(), style)
+ Self::from_ast(basedir, vec![filename], ast.to_pandoc(), style, template)
}
/// Return the AST of a Document, serialized as JSON.
@@ -451,7 +459,7 @@ impl<'a> Document {
///
/// This version uses Pandoc to parse the Markdown.
#[instrument(level = "trace")]
-pub fn load_document<P>(filename: P, style: Style) -> Result<Document>
+pub fn load_document<P>(filename: P, style: Style, template: Option<&str>) -> Result<Document>
where
P: AsRef<Path> + Debug,
{
@@ -466,7 +474,7 @@ where
filename.display(),
style
);
- let doc = Document::from_file(&base_path, filename, style)?;
+ let doc = Document::from_file(&base_path, filename, style, template)?;
event!(Level::TRACE, "Loaded doc from file OK");
Ok(doc)
@@ -476,7 +484,11 @@ where
///
/// This version uses the `cmark-pullmark` crate to parse Markdown.
#[instrument(level = "trace")]
-pub fn load_document_with_pullmark<P>(filename: P, style: Style) -> Result<Document>
+pub fn load_document_with_pullmark<P>(
+ filename: P,
+ style: Style,
+ template: Option<&str>,
+) -> Result<Document>
where
P: AsRef<Path> + Debug,
{
@@ -492,7 +504,7 @@ where
style
);
crate::resource::add_search_path(filename.parent().unwrap());
- let doc = Document::from_file_with_pullmark(&base_path, filename, style)?;
+ let doc = Document::from_file_with_pullmark(&base_path, filename, style, template)?;
event!(Level::TRACE, "Loaded doc from file OK");
Ok(doc)
}
@@ -502,7 +514,7 @@ pub fn codegen(filename: &Path, output: &Path, template: Option<&str>) -> Result
let span = span!(Level::TRACE, "codegen");
let _enter = span.enter();
- let mut doc = load_document_with_pullmark(filename, Style::default())?;
+ let mut doc = load_document_with_pullmark(filename, Style::default(), template)?;
doc.lint()?;
let template = template
.map(Ok)
diff --git a/src/metadata.rs b/src/metadata.rs
index 03d4eff..0d0e844 100644
--- a/src/metadata.rs
+++ b/src/metadata.rs
@@ -32,14 +32,13 @@ pub struct DocumentImpl {
impl Metadata {
/// Construct a Metadata from a Document, if possible.
#[instrument(level = "trace", skip(doc))]
- pub fn new<P>(basedir: P, doc: &Pandoc) -> Result<Metadata>
+ pub fn new<P>(basedir: P, doc: &Pandoc, template: Option<&str>) -> Result<Metadata>
where
P: AsRef<Path> + Debug,
{
let title = get_title(&doc.meta);
let date = get_date(&doc.meta);
let bindings_filenames = get_bindings_filenames(&doc.meta);
- let functions_filenames = get_functions_filenames(&doc.meta);
let bibliographies = get_bibliographies(basedir.as_ref(), &doc.meta);
let classes = get_classes(&doc.meta);
event!(
@@ -47,31 +46,41 @@ impl Metadata {
?title,
?date,
?bindings_filenames,
- ?functions_filenames,
?bibliographies,
?classes,
"Loaded basic metadata"
);
- let (template, spec) = if let Some((template, spec)) = get_template_spec(&doc.meta)? {
- (Some(template), Some(spec))
- } else {
- (None, None)
- };
- event!(Level::TRACE, ?template, ?spec, "Loaded template spec");
- let mut bindings = Bindings::new();
- get_bindings(&bindings_filenames, &mut bindings, template.as_deref())?;
- event!(Level::TRACE, "Loaded all metadata successfully");
let mut impls = HashMap::new();
- if let (Some(template), Some(spec)) = (template, spec) {
- let mut docimpl = DocumentImpl::new(spec);
- for fname in functions_filenames {
- docimpl.add_function_file(fname);
+ if let Some(raw_impls) = doc.meta.get("impls") {
+ match raw_impls {
+ MetaValue::MetaMap(raw_impls) => {
+ for (impl_name, functions_filenames) in raw_impls.iter() {
+ let template_spec = load_template_spec(impl_name)?;
+ let filenames = pathbufs("", functions_filenames);
+ let docimpl = DocumentImpl::new(template_spec, filenames);
+ impls.insert(impl_name.to_string(), docimpl);
+ }
+ }
+ _ => {
+ event!(
+ Level::WARN,
+ value = ?raw_impls,
+ "Ignoring unknown raw implementation value"
+ );
+ }
}
- impls.insert(template, docimpl);
}
+ let template = template.or_else(|| impls.keys().next().map(String::as_str));
+
+ let mut bindings = Bindings::new();
+
+ get_bindings(&bindings_filenames, &mut bindings, template.as_deref())?;
+
+ event!(Level::TRACE, "Loaded all metadata successfully");
+
Ok(Metadata {
title,
date,
@@ -125,15 +134,8 @@ impl Metadata {
}
impl DocumentImpl {
- fn new(spec: TemplateSpec) -> Self {
- Self {
- spec,
- functions: Vec::new(),
- }
- }
-
- fn add_function_file(&mut self, function: PathBuf) {
- self.functions.push(function);
+ fn new(spec: TemplateSpec, functions: Vec<PathBuf>) -> Self {
+ Self { spec, functions }
}
pub fn functions_filenames(&self) -> impl Iterator<Item = &Path> {
@@ -163,21 +165,11 @@ fn get_bindings_filenames(map: &Mapp) -> Vec<PathBuf> {
get_paths("", map, "bindings")
}
-fn get_functions_filenames(map: &Mapp) -> Vec<PathBuf> {
- get_paths("", map, "functions")
-}
-
-fn get_template_spec(map: &Mapp) -> Result<Option<(String, TemplateSpec)>> {
- match get_string(map, "template") {
- Some(s) => {
- let mut spec_path = PathBuf::from(&s);
- spec_path.push("template");
- spec_path.push("template.yaml");
- let spec = TemplateSpec::from_file(&spec_path)?;
- Ok(Some((s, spec)))
- }
- None => Ok(None),
- }
+fn load_template_spec(template: &str) -> Result<TemplateSpec> {
+ let mut spec_path = PathBuf::from(template);
+ spec_path.push("template");
+ spec_path.push("template.yaml");
+ TemplateSpec::from_file(&spec_path)
}
fn get_paths<P>(basedir: P, map: &Mapp, field: &str) -> Vec<PathBuf>