From 1898ff35ddbc717304c2afd1e25257013919b995 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Sat, 3 Sep 2022 11:19:47 +0300 Subject: refactor: expose functionality to extract embedded YAML from md Sponsored-by: author --- src/ast.rs | 88 +++++++++++++++++++++++++------------------------------------- src/lib.rs | 2 +- 2 files changed, 36 insertions(+), 54 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 4fba52e..afa8b3b 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -51,27 +51,33 @@ impl std::str::FromStr for AbstractSyntaxTree { /// Create an abstract syntax tree from a string. fn from_str(markdown: &str) -> Result { - 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, author: Option, @@ -399,33 +408,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"; diff --git a/src/lib.rs b/src/lib.rs index 1d0060b..725e49c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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}; -- cgit v1.2.1