use crate::{Bindings, SubplotError, TemplateSpec, YamlMetadata}; use log::trace; use std::collections::HashMap; use std::fmt::Debug; use std::ops::Deref; use std::path::{Path, PathBuf}; /// Metadata of a document, as needed by Subplot. #[derive(Debug)] pub struct Metadata { basedir: PathBuf, title: String, date: Option, markdown_filename: PathBuf, bindings_filenames: Vec, bindings: Bindings, impls: HashMap, bibliographies: Vec, /// Extra class names which should be considered 'correct' for this document classes: Vec, } #[derive(Debug)] pub struct DocumentImpl { spec: TemplateSpec, functions: Vec, } impl Metadata { /// Create from YamlMetadata. pub fn from_yaml_metadata

( basedir: P, yaml: &YamlMetadata, template: Option<&str>, ) -> Result where P: AsRef + Debug, { let mut bindings = Bindings::new(); let bindings_filenames = if let Some(filenames) = yaml.bindings_filenames() { get_bindings(filenames, &mut bindings, template)?; filenames.iter().map(|p| p.to_path_buf()).collect() } else { vec![] }; let mut impls = HashMap::new(); for (impl_name, functions_filenames) in yaml.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); } let bibliographies = if let Some(v) = yaml.bibliographies() { v.iter().map(|s| s.to_path_buf()).collect() } else { vec![] }; let classes = if let Some(v) = yaml.classes() { v.iter().map(|s| s.to_string()).collect() } else { vec![] }; let meta = Self { basedir: basedir.as_ref().to_path_buf(), title: yaml.title().into(), date: yaml.date().map(|s| s.into()), markdown_filename: yaml.markdown().into(), bindings_filenames, bindings, impls, bibliographies, classes, }; trace!("metadata: {:#?}", meta); Ok(meta) } /// Return title of document. pub fn title(&self) -> &str { &self.title } /// Return date of document, if any. pub fn date(&self) -> Option<&str> { self.date.as_deref() } /// Return base dir for all relative filenames. pub fn basedir(&self) -> &Path { &self.basedir } /// Return filename of the markdown file. pub fn markdown_filename(&self) -> &Path { &self.markdown_filename } /// Return filename where bindings are specified. pub fn bindings_filenames(&self) -> Vec<&Path> { self.bindings_filenames.iter().map(|f| f.as_ref()).collect() } /// Return the document implementation (filenames, spec, etc) for the given template name pub fn document_impl(&self, template: &str) -> Option<&DocumentImpl> { self.impls.get(template) } /// Return the templates the document expects to implement pub fn templates(&self) -> impl Iterator { self.impls.keys().map(String::as_str) } /// 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 { self.classes.iter().map(Deref::deref) } } impl DocumentImpl { fn new(spec: TemplateSpec, functions: Vec) -> Self { Self { spec, functions } } pub fn functions_filenames(&self) -> impl Iterator { self.functions.iter().map(PathBuf::as_path) } pub fn spec(&self) -> &TemplateSpec { &self.spec } } fn load_template_spec(template: &str) -> Result { let mut spec_path = PathBuf::from(template); spec_path.push("template"); spec_path.push("template.yaml"); TemplateSpec::from_file(&spec_path) } fn pathbufs

(basedir: P, v: &[PathBuf]) -> Vec where P: AsRef, { let basedir = basedir.as_ref(); v.iter().map(|p| basedir.join(p)).collect() } fn get_bindings

( filenames: &[P], bindings: &mut Bindings, template: Option<&str>, ) -> Result<(), SubplotError> where P: AsRef + Debug, { for filename in filenames { bindings.add_from_file(filename, template)?; } Ok(()) }