From eb304836c4b1cf3ee1c60c9618d2e9fca15838ce Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Wed, 27 Dec 2023 13:20:38 -0800 Subject: [PATCH] Add a Block enum type which will eventually represent all possible block types in AsciiDoc --- src/blocks/block.rs | 35 ++++++++++++++ src/blocks/mod.rs | 6 ++- src/tests/blocks/block.rs | 95 ++++++++++++++++++++++++++++++++++++++ src/tests/blocks/mod.rs | 3 +- src/tests/blocks/simple.rs | 2 +- 5 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 src/blocks/block.rs create mode 100644 src/tests/blocks/block.rs diff --git a/src/blocks/block.rs b/src/blocks/block.rs new file mode 100644 index 0000000..ce414bc --- /dev/null +++ b/src/blocks/block.rs @@ -0,0 +1,35 @@ +use nom::IResult; + +use super::SimpleBlock; +use crate::{primitives::consume_empty_lines, Span}; + +/// Block elements form the main structure of an AsciiDoc document, starting +/// with the document itself. +/// +/// A block element (aka block) is a discrete, line-oriented chunk of content in +/// an AsciiDoc document. Once parsed, that chunk of content becomes a block +/// element in the parsed document model. Certain blocks may contain other +/// blocks, so we say that blocks can be nested. The converter visits each block +/// in turn, in document order, converting it to a corresponding chunk of +/// output. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Block<'a> { + /// A block that’s treated as contiguous lines of paragraph text (and + /// subject to normal substitutions) (e.g., a paragraph block). + Simple(SimpleBlock<'a>), +} + +impl<'a> Block<'a> { + #[allow(dead_code)] + /// Parse a block of any type and return a `Block` that describes it. + /// + /// Consumes any blank lines before and after the block. + pub(crate) fn parse(i: Span<'a>) -> IResult { + let i = consume_empty_lines(i); + + // TEMPORARY: So far, we only know SimpleBlock. + // Later we'll start to try to discern other block types. + let (rem, simple_block) = SimpleBlock::parse(i)?; + Ok((rem, Self::Simple(simple_block))) + } +} diff --git a/src/blocks/mod.rs b/src/blocks/mod.rs index a94714a..4f5d38a 100644 --- a/src/blocks/mod.rs +++ b/src/blocks/mod.rs @@ -8,4 +8,8 @@ //! in turn, in document order, converting it to a corresponding chunk of //! output. -pub(crate) mod simple; +mod block; +pub use block::Block; + +mod simple; +pub use simple::SimpleBlock; diff --git a/src/tests/blocks/block.rs b/src/tests/blocks/block.rs new file mode 100644 index 0000000..4b632bf --- /dev/null +++ b/src/tests/blocks/block.rs @@ -0,0 +1,95 @@ +mod simple { + use nom::{ + error::{Error, ErrorKind}, + Err, + }; + + use crate::{ + blocks::{Block, SimpleBlock}, + Span, + }; + + #[test] + fn empty_source() { + let expected_err = Err::Error(Error::new(Span::new("", true), ErrorKind::TakeTill1)); + + let actual_err = Block::parse(Span::new("", true)).unwrap_err(); + + assert_eq!(expected_err, actual_err); + } + + #[test] + fn only_spaces() { + let err = Block::parse(Span::new(" ", true)).unwrap_err(); + + let Err::Error(e) = err else { + panic!("Expected Err::Error: {err:#?}"); + }; + + assert_eq!(e.code, ErrorKind::TakeTill1); + + let span = e.input; + assert_eq!(span.data(), &""); + assert_eq!(span.line(), 1); + assert_eq!(span.col(), 5); + } + + #[test] + fn single_line() { + let expected = Block::Simple(SimpleBlock { + inlines: vec![Span::new("abc", true)], + }); + + let (rem, block) = Block::parse(Span::new("abc", true)).unwrap(); + + assert_eq!(rem.line(), 1); + assert_eq!(rem.col(), 4); + assert_eq!(*rem.data(), ""); + + assert_eq!(block, expected); + } + + #[test] + fn multiple_lines() { + let (rem, block) = Block::parse(Span::new("abc\ndef", true)).unwrap(); + + assert_eq!(rem.line(), 2); + assert_eq!(rem.col(), 4); + assert_eq!(*rem.data(), ""); + + let Block::Simple(block) = block; + // else { // ADD THIS ONCE WE HAVE OTHER BLOCK TYPES + //panic!("Expected a SimpleBlock: {block:#?}"); + //}; + + assert_eq!(block.inlines.len(), 2); + + assert_eq!(block.inlines[0].line(), 1); + assert_eq!(block.inlines[0].col(), 1); + assert_eq!(*block.inlines[0].data(), "abc"); + + assert_eq!(block.inlines[1].line(), 2); + assert_eq!(block.inlines[1].col(), 1); + assert_eq!(*block.inlines[1].data(), "def"); + } + + #[test] + fn consumes_blank_lines_after() { + let expected = SimpleBlock { + inlines: vec![Span::new("abc", true)], + }; + + let (rem, block) = Block::parse(Span::new("abc\n\ndef", true)).unwrap(); + + assert_eq!(rem.line(), 3); + assert_eq!(rem.col(), 1); + assert_eq!(*rem.data(), "def"); + + let Block::Simple(block) = block; + // else { // ADD THIS ONCE WE HAVE OTHER BLOCK TYPES + //panic!("Expected a SimpleBlock: {block:#?}"); + //}; + + assert_eq!(block, expected); + } +} diff --git a/src/tests/blocks/mod.rs b/src/tests/blocks/mod.rs index 6d0ed9e..68a05a7 100644 --- a/src/tests/blocks/mod.rs +++ b/src/tests/blocks/mod.rs @@ -1 +1,2 @@ -pub(crate) mod simple; +mod block; +mod simple; diff --git a/src/tests/blocks/simple.rs b/src/tests/blocks/simple.rs index c27e6e6..1b78f68 100644 --- a/src/tests/blocks/simple.rs +++ b/src/tests/blocks/simple.rs @@ -3,7 +3,7 @@ use nom::{ Err, }; -use crate::{blocks::simple::SimpleBlock, Span}; +use crate::{blocks::SimpleBlock, Span}; #[test] fn empty_source() {