summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2022-09-03 11:19:47 +0300
committerLars Wirzenius <liw@liw.fi>2022-09-03 11:19:47 +0300
commit1898ff35ddbc717304c2afd1e25257013919b995 (patch)
tree6ec6d0576a7849b092728effcf2b660eb437c9e4
parent32a3e2a805c2470bd460d74f36f0b49b51453e62 (diff)
downloadsubplot-1898ff35ddbc717304c2afd1e25257013919b995.tar.gz
refactor: expose functionality to extract embedded YAML from md
Sponsored-by: author
-rw-r--r--src/ast.rs88
-rw-r--r--src/lib.rs2
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<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();
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};