diff options
author | Lars Wirzenius <liw@liw.fi> | 2022-09-03 11:19:47 +0300 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2022-09-03 11:19:47 +0300 |
commit | 1898ff35ddbc717304c2afd1e25257013919b995 (patch) | |
tree | 6ec6d0576a7849b092728effcf2b660eb437c9e4 | |
parent | 32a3e2a805c2470bd460d74f36f0b49b51453e62 (diff) | |
download | subplot-1898ff35ddbc717304c2afd1e25257013919b995.tar.gz |
refactor: expose functionality to extract embedded YAML from md
Sponsored-by: author
-rw-r--r-- | src/ast.rs | 88 | ||||
-rw-r--r-- | src/lib.rs | 2 |
2 files changed, 36 insertions, 54 deletions
@@ -51,27 +51,33 @@ impl std::str::FromStr for AbstractSyntaxTree { /// Create an abstract syntax tree from a string. fn from_str(markdown: &str) -> Result<Self, Self::Err> { - trace!("Parsing markdown"); - let ast = if let Some((yaml, markdown)) = get_yaml(&LEADING_YAML_PATTERN, markdown) { - trace!("Found leading YAML: {:?}", yaml); - let meta = YamlMetadata::new(yaml)?; - let blocks = parse_blocks(markdown); - AbstractSyntaxTree::new(meta, blocks) - } else if let Some((yaml, _markdown)) = get_yaml(&TRAILING_YAML_PATTERN, markdown) { - trace!("Found trailing YAML: {:?}", yaml); - let meta = YamlMetadata::new(yaml)?; - let blocks = parse_blocks(markdown); - AbstractSyntaxTree::new(meta, blocks) - } else { - trace!("No YAML to be found"); - let blocks = parse_blocks(markdown); - AbstractSyntaxTree::new(YamlMetadata::default(), blocks) - }; + trace!("Parsing markdown into AST"); + let (meta, markdown) = extract_metadata(markdown)?; + let blocks = parse_blocks(markdown); + let ast = AbstractSyntaxTree::new(meta, blocks); trace!("Parsing markdown: OK"); Ok(ast) } } +/// Extract YAML metadata from a Markdown document. +pub fn extract_metadata(markdown: &str) -> Result<(YamlMetadata, &str), Error> { + trace!("Extracting YAML from Markdown"); + let (yaml, md) = if let Some((yaml, markdown)) = get_yaml(&LEADING_YAML_PATTERN, markdown) { + trace!("Found leading YAML: {:?}", yaml); + (yaml, markdown) + } else if let Some((yaml, _markdown)) = get_yaml(&TRAILING_YAML_PATTERN, markdown) { + trace!("Found trailing YAML: {:?}", yaml); + (yaml, markdown) + } else { + trace!("No YAML to be found"); + return Err(Error::NoMetadata); + }; + let meta = YamlMetadata::new(yaml)?; + trace!("Parsing markdown: OK"); + Ok((meta, md)) +} + // Extract a YAML metadata block using a given regex. fn get_yaml<'a>(pat: &Regex, markdown: &'a str) -> Option<(&'a str, &'a str)> { trace!("Markdown: {:?}", markdown); @@ -272,22 +278,25 @@ 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. +/// 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, Deserialize)] #[serde(deny_unknown_fields)] -struct YamlMetadata { +pub struct YamlMetadata { title: String, subtitle: Option<String>, author: Option<String>, @@ -400,33 +409,6 @@ mod test { } #[test] - fn empty_input() { - let ast = AbstractSyntaxTree::from_str("").unwrap(); - let doc = ast.to_pandoc(); - assert!(doc.blocks.is_empty()); - assert!(!doc.pandoc_api_version.is_empty()); - } - - #[test] - fn simple() { - let ast = AbstractSyntaxTree::from_str( - "\ - # Introduction \n\ - \n\ - First paragraph.\n\ - ", - ) - .unwrap(); - let doc = ast.to_pandoc(); - assert!(!doc.pandoc_api_version.is_empty()); - - let attr = ("".to_string(), vec![], vec![]); - let h = Block::Header(1, attr, vec![Inline::Str("Introduction".to_string())]); - let para = Block::Para(vec![Inline::Str("First paragraph.".to_string())]); - assert_eq!(doc.blocks, &[h, para]); - } - - #[test] fn parses_leading_meta() { let markdown = "\n\n---\ntitle: Foo Bar\n...\nfoobar\n"; let ast = AbstractSyntaxTree::from_str(markdown).unwrap(); @@ -73,4 +73,4 @@ mod codegen; pub use codegen::generate_test_program; mod ast; -pub use ast::AbstractSyntaxTree; +pub use ast::{extract_metadata, AbstractSyntaxTree, YamlMetadata}; |