diff options
author | Daniel Silverstone <dsilvers+gitlab@digital-scurf.org> | 2023-01-28 10:15:25 +0000 |
---|---|---|
committer | Daniel Silverstone <dsilvers+gitlab@digital-scurf.org> | 2023-01-28 10:15:25 +0000 |
commit | e1d63019fdd6f0d24703197b6a6029f26e6f089f (patch) | |
tree | 82483e5dad773cbf90884716e66a47629aa487fa | |
parent | fa5765189eef33bed301479410c4a53dc274acf0 (diff) | |
parent | 387f0b7675fba94115ead348c358c7d3e7638e4c (diff) | |
download | subplot-e1d63019fdd6f0d24703197b6a6029f26e6f089f.tar.gz |
Merge branch 'liw/refactor-metadata' into 'main'
refactor: move YamlMetadata to src/metadata.rs
See merge request subplot/subplot!306
-rw-r--r-- | src/ast.rs | 172 | ||||
-rw-r--r-- | src/doc.rs | 8 | ||||
-rw-r--r-- | src/error.rs | 2 | ||||
-rw-r--r-- | src/lib.rs | 5 | ||||
-rw-r--r-- | src/metadata.rs | 174 |
5 files changed, 177 insertions, 184 deletions
diff --git a/src/ast.rs b/src/ast.rs deleted file mode 100644 index ed163f0..0000000 --- a/src/ast.rs +++ /dev/null @@ -1,172 +0,0 @@ -use lazy_static::lazy_static; -use regex::Regex; -use serde::Deserialize; -use serde_yaml::Value; -use std::collections::{BTreeMap, HashMap}; -use std::path::{Path, PathBuf}; - -lazy_static! { - // Pattern that recognises a YAML block at the beginning of a file. - static ref LEADING_YAML_PATTERN: Regex = Regex::new(r"^(?:\S*\n)*(?P<yaml>-{3,}\n([^.].*\n)*\.{3,}\n)(?P<text>(.*\n)*)$").unwrap(); - - - // Pattern that recognises a YAML block at the end of a file. - static ref TRAILING_YAML_PATTERN: Regex = Regex::new(r"(?P<text>(.*\n)*)\n*(?P<yaml>-{3,}\n([^.].*\n)*\.{3,}\n)(?:\S*\n)*$").unwrap(); -} - -/// Errors from Markdown parsing. -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error(transparent)] - Regex(#[from] regex::Error), - - #[error("Markdown doesn't contain a YAML block for document metadata")] - NoMetadata, - - #[error(transparent)] - Yaml(#[from] serde_yaml::Error), -} - -/// Document metadata. -/// -/// This is expressed in the Markdown input file as an embedded YAML -/// block. -/// -/// Note that this structure needs to be able to capture any metadata -/// block we can work with, in any input file. By being strict here we -/// make it easier to tell the user when a metadata block has, say, a -/// misspelled field. -#[derive(Debug, Default, Clone, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct YamlMetadata { - title: String, - subtitle: Option<String>, - authors: Option<Vec<String>>, - date: Option<String>, - classes: Option<Vec<String>>, - bibliography: Option<Vec<PathBuf>>, - markdowns: Vec<PathBuf>, - bindings: Option<Vec<PathBuf>>, - documentclass: Option<String>, - #[serde(default)] - impls: BTreeMap<String, Vec<PathBuf>>, - pandoc: Option<HashMap<String, Value>>, -} - -impl YamlMetadata { - #[cfg(test)] - fn new(yaml_text: &str) -> Result<Self, Error> { - let meta: Self = serde_yaml::from_str(yaml_text)?; - Ok(meta) - } - - /// Name of file with the Markdown for the subplot document. - pub fn markdown(&self) -> &Path { - &self.markdowns[0] - } - - /// Title. - pub fn title(&self) -> &str { - &self.title - } - - /// Subtitle. - pub fn subtitle(&self) -> Option<&str> { - self.subtitle.as_deref() - } - - /// Date. - pub fn date(&self) -> Option<&str> { - self.date.as_deref() - } - - /// Authors. - pub fn authors(&self) -> Option<&[String]> { - self.authors.as_deref() - } - - /// Names of bindings files. - pub fn bindings_filenames(&self) -> Option<&[PathBuf]> { - self.bindings.as_deref() - } - - /// Impls section. - pub fn impls(&self) -> &BTreeMap<String, Vec<PathBuf>> { - &self.impls - } - - /// Bibliographies. - pub fn bibliographies(&self) -> Option<&[PathBuf]> { - self.bibliography.as_deref() - } - - /// Classes.. - pub fn classes(&self) -> Option<&[String]> { - self.classes.as_deref() - } - - /// Documentclass. - pub fn documentclass(&self) -> Option<&str> { - self.documentclass.as_deref() - } - - /// Pandoc metadata. - pub fn pandoc(&self) -> Option<&HashMap<String, Value>> { - if let Some(x) = &self.pandoc { - Some(x) - } else { - None - } - } -} - -#[cfg(test)] -mod test { - use super::YamlMetadata; - use std::path::{Path, PathBuf}; - - #[test] - fn full_meta() { - let meta = YamlMetadata::new( - "\ -title: Foo Bar -date: today -classes: [json, text] -impls: - python: - - foo.py - - bar.py -bibliography: -- foo.bib -- bar.bib -markdowns: -- test.md -bindings: -- foo.yaml -- bar.yaml -", - ) - .unwrap(); - assert_eq!(meta.title, "Foo Bar"); - assert_eq!(meta.date.unwrap(), "today"); - assert_eq!(meta.classes.unwrap(), &["json", "text"]); - assert_eq!( - meta.bibliography.unwrap(), - &[path("foo.bib"), path("bar.bib")] - ); - assert_eq!(meta.markdowns, vec![Path::new("test.md")]); - assert_eq!( - meta.bindings.unwrap(), - &[path("foo.yaml"), path("bar.yaml")] - ); - 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 { - PathBuf::from(s) - } -} @@ -1,4 +1,3 @@ -use crate::ast; use crate::bindings::CaptureType; use crate::generate_test_program; use crate::get_basedir_from; @@ -6,12 +5,11 @@ use crate::md::Markdown; use crate::EmbeddedFile; use crate::EmbeddedFiles; use crate::MatchedScenario; -use crate::Metadata; use crate::PartialStep; use crate::Scenario; use crate::Style; use crate::SubplotError; -use crate::YamlMetadata; +use crate::{Metadata, YamlMetadata}; use crate::{Warning, Warnings}; use std::collections::HashSet; @@ -106,7 +104,7 @@ impl Document { basedir: P, subplot: PathBuf, markdowns: Vec<PathBuf>, - yamlmeta: &ast::YamlMetadata, + yamlmeta: &YamlMetadata, mut md: Markdown, style: Style, template: Option<&str>, @@ -422,7 +420,7 @@ impl Document { fn load_metadata_from_yaml_file(filename: &Path) -> Result<YamlMetadata, SubplotError> { let yaml = read(filename).map_err(|e| SubplotError::ReadFile(filename.into(), e))?; trace!("Parsing YAML metadata from {}", filename.display()); - let meta: ast::YamlMetadata = serde_yaml::from_slice(&yaml) + let meta: YamlMetadata = serde_yaml::from_slice(&yaml) .map_err(|e| SubplotError::MetadataFile(filename.into(), e))?; Ok(meta) } diff --git a/src/error.rs b/src/error.rs index a729bf0..0ac2abf 100644 --- a/src/error.rs +++ b/src/error.rs @@ -308,7 +308,7 @@ pub enum SubplotError { /// Abstract syntax tree error. #[error(transparent)] - Ast(#[from] crate::ast::Error), + Ast(#[from] crate::metadata::Error), /// UTF8 conversion error. #[error("failed to parse UTF8 in file {0}")] @@ -37,7 +37,7 @@ mod policy; pub use policy::get_basedir_from; mod metadata; -pub use metadata::Metadata; +pub use metadata::{Metadata, YamlMetadata}; mod doc; pub mod md; @@ -72,6 +72,3 @@ pub use templatespec::TemplateSpec; mod codegen; pub use codegen::generate_test_program; - -mod ast; -pub use ast::YamlMetadata; diff --git a/src/metadata.rs b/src/metadata.rs index e88c732..b840633 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -1,11 +1,181 @@ -use crate::{Bindings, SubplotError, TemplateSpec, YamlMetadata}; +use crate::{Bindings, SubplotError, TemplateSpec}; +use lazy_static::lazy_static; use log::trace; -use std::collections::HashMap; +use regex::Regex; +use serde::Deserialize; +use serde_yaml::Value; +use std::collections::{BTreeMap, HashMap}; use std::fmt::Debug; use std::ops::Deref; use std::path::{Path, PathBuf}; +lazy_static! { + // Pattern that recognises a YAML block at the beginning of a file. + static ref LEADING_YAML_PATTERN: Regex = Regex::new(r"^(?:\S*\n)*(?P<yaml>-{3,}\n([^.].*\n)*\.{3,}\n)(?P<text>(.*\n)*)$").unwrap(); + + + // Pattern that recognises a YAML block at the end of a file. + static ref TRAILING_YAML_PATTERN: Regex = Regex::new(r"(?P<text>(.*\n)*)\n*(?P<yaml>-{3,}\n([^.].*\n)*\.{3,}\n)(?:\S*\n)*$").unwrap(); +} + +/// Errors from Markdown parsing. +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error(transparent)] + Regex(#[from] regex::Error), + + #[error("Markdown doesn't contain a YAML block for document metadata")] + NoMetadata, + + #[error(transparent)] + Yaml(#[from] serde_yaml::Error), +} + +/// Document metadata. +/// +/// This is expressed in the Markdown input file as an embedded YAML +/// block. +/// +/// Note that this structure needs to be able to capture any metadata +/// block we can work with, in any input file. By being strict here we +/// make it easier to tell the user when a metadata block has, say, a +/// misspelled field. +#[derive(Debug, Default, Clone, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct YamlMetadata { + title: String, + subtitle: Option<String>, + authors: Option<Vec<String>>, + date: Option<String>, + classes: Option<Vec<String>>, + bibliography: Option<Vec<PathBuf>>, + markdowns: Vec<PathBuf>, + bindings: Option<Vec<PathBuf>>, + documentclass: Option<String>, + #[serde(default)] + impls: BTreeMap<String, Vec<PathBuf>>, + pandoc: Option<HashMap<String, Value>>, +} + +impl YamlMetadata { + #[cfg(test)] + fn new(yaml_text: &str) -> Result<Self, Error> { + let meta: Self = serde_yaml::from_str(yaml_text)?; + Ok(meta) + } + + /// Name of file with the Markdown for the subplot document. + pub fn markdown(&self) -> &Path { + &self.markdowns[0] + } + + /// Title. + pub fn title(&self) -> &str { + &self.title + } + + /// Subtitle. + pub fn subtitle(&self) -> Option<&str> { + self.subtitle.as_deref() + } + + /// Date. + pub fn date(&self) -> Option<&str> { + self.date.as_deref() + } + + /// Authors. + pub fn authors(&self) -> Option<&[String]> { + self.authors.as_deref() + } + + /// Names of bindings files. + pub fn bindings_filenames(&self) -> Option<&[PathBuf]> { + self.bindings.as_deref() + } + + /// Impls section. + pub fn impls(&self) -> &BTreeMap<String, Vec<PathBuf>> { + &self.impls + } + + /// Bibliographies. + pub fn bibliographies(&self) -> Option<&[PathBuf]> { + self.bibliography.as_deref() + } + + /// Classes.. + pub fn classes(&self) -> Option<&[String]> { + self.classes.as_deref() + } + + /// Documentclass. + pub fn documentclass(&self) -> Option<&str> { + self.documentclass.as_deref() + } + + /// Pandoc metadata. + pub fn pandoc(&self) -> Option<&HashMap<String, Value>> { + if let Some(x) = &self.pandoc { + Some(x) + } else { + None + } + } +} + +#[cfg(test)] +mod test { + use super::YamlMetadata; + use std::path::{Path, PathBuf}; + + #[test] + fn full_meta() { + let meta = YamlMetadata::new( + "\ +title: Foo Bar +date: today +classes: [json, text] +impls: + python: + - foo.py + - bar.py +bibliography: +- foo.bib +- bar.bib +markdowns: +- test.md +bindings: +- foo.yaml +- bar.yaml +", + ) + .unwrap(); + assert_eq!(meta.title, "Foo Bar"); + assert_eq!(meta.date.unwrap(), "today"); + assert_eq!(meta.classes.unwrap(), &["json", "text"]); + assert_eq!( + meta.bibliography.unwrap(), + &[path("foo.bib"), path("bar.bib")] + ); + assert_eq!(meta.markdowns, vec![Path::new("test.md")]); + assert_eq!( + meta.bindings.unwrap(), + &[path("foo.yaml"), path("bar.yaml")] + ); + 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 { + PathBuf::from(s) + } +} + /// Metadata of a document, as needed by Subplot. #[derive(Debug)] pub struct Metadata { |