diff --git a/src/document/document.rs b/src/document/document.rs new file mode 100644 index 0000000..0e87543 --- /dev/null +++ b/src/document/document.rs @@ -0,0 +1,65 @@ +//! Describes the top-level document structure. + +use std::slice::Iter; + +use nom::IResult; + +use crate::{blocks::Block, primitives::consume_empty_lines, Error, HasSpan, Span}; + +/// A document represents the top-level block element in AsciiDoc. It consists +/// of an optional document header and either a) one or more sections preceded +/// by an optional preamble or b) a sequence of top-level blocks only. +/// +/// The document can be configured using a document header. The header is not a +/// block itself, but contributes metadata to the document, such as the document +/// title and document attributes. +#[allow(dead_code)] // TEMPORARY while building +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Document<'a> { + blocks: Vec>, + source: Span<'a>, +} + +impl<'a> Document<'a> { + /// Parse a UTF-8 string as an AsciiDoc document. + /// + /// Note that the document references the underlying source string and + /// necessarily has the same lifetime as the source. + pub fn parse(source: &'a str) -> Result { + let source = Span::new(source, true); + let i = source; + + // TO DO: Look for document header. + // TO DO: Add option for best-guess parsing? + + let (_rem, blocks) = parse_blocks(i)?; + + // let blocks: Vec> = vec![]; // TEMPORARY + Ok(Self { source, blocks }) + } + + /// Return an iterator over the blocks in this document. + pub fn blocks(&'a self) -> Iter<'a, Block<'a>> { + self.blocks.iter() + } +} + +impl<'a> HasSpan<'a> for Document<'a> { + fn span(&'a self) -> &'a Span<'a> { + &self.source + } +} + +fn parse_blocks<'a>(mut i: Span<'a>) -> IResult>> { + let mut blocks: Vec> = vec![]; + i = consume_empty_lines(i); + + while !i.data().is_empty() { + // TO DO: Handle other kinds of blocks. + let (i2, block) = Block::parse(i)?; + i = i2; + blocks.push(block); + } + + Ok((i, blocks)) +} diff --git a/src/document/mod.rs b/src/document/mod.rs index 0e87543..5865d82 100644 --- a/src/document/mod.rs +++ b/src/document/mod.rs @@ -1,65 +1,8 @@ //! Describes the top-level document structure. -use std::slice::Iter; - -use nom::IResult; - -use crate::{blocks::Block, primitives::consume_empty_lines, Error, HasSpan, Span}; - -/// A document represents the top-level block element in AsciiDoc. It consists -/// of an optional document header and either a) one or more sections preceded -/// by an optional preamble or b) a sequence of top-level blocks only. -/// -/// The document can be configured using a document header. The header is not a -/// block itself, but contributes metadata to the document, such as the document -/// title and document attributes. -#[allow(dead_code)] // TEMPORARY while building -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Document<'a> { - blocks: Vec>, - source: Span<'a>, -} - -impl<'a> Document<'a> { - /// Parse a UTF-8 string as an AsciiDoc document. - /// - /// Note that the document references the underlying source string and - /// necessarily has the same lifetime as the source. - pub fn parse(source: &'a str) -> Result { - let source = Span::new(source, true); - let i = source; - - // TO DO: Look for document header. - // TO DO: Add option for best-guess parsing? - - let (_rem, blocks) = parse_blocks(i)?; - - // let blocks: Vec> = vec![]; // TEMPORARY - Ok(Self { source, blocks }) - } - - /// Return an iterator over the blocks in this document. - pub fn blocks(&'a self) -> Iter<'a, Block<'a>> { - self.blocks.iter() - } -} - -impl<'a> HasSpan<'a> for Document<'a> { - fn span(&'a self) -> &'a Span<'a> { - &self.source - } -} - -fn parse_blocks<'a>(mut i: Span<'a>) -> IResult>> { - let mut blocks: Vec> = vec![]; - i = consume_empty_lines(i); - - while !i.data().is_empty() { - // TO DO: Handle other kinds of blocks. - let (i2, block) = Block::parse(i)?; - i = i2; - blocks.push(block); - } - - Ok((i, blocks)) -} +// I understand the purpose behind this warning, but +// this module/submodule layout feels preferable in this +// circumstance. +#[allow(clippy::module_inception)] +mod document; +pub use document::Document; diff --git a/src/tests/document/document.rs b/src/tests/document/document.rs new file mode 100644 index 0000000..92f10f2 --- /dev/null +++ b/src/tests/document/document.rs @@ -0,0 +1,116 @@ +use pretty_assertions_sorted::assert_eq; + +use crate::{ + document::Document, + tests::fixtures::{ + blocks::{TBlock, TSimpleBlock}, + document::TDocument, + TSpan, + }, +}; + +#[test] +fn empty_source() { + assert_eq!( + Document::parse("").unwrap(), + TDocument { + source: TSpan { + data: "", + line: 1, + col: 1, + offset: 0 + }, + blocks: vec![], + } + ); +} + +#[test] +fn only_spaces() { + assert_eq!( + Document::parse(" ").unwrap(), + TDocument { + source: TSpan { + data: " ", + line: 1, + col: 1, + offset: 0 + }, + blocks: vec![], + } + ); +} + +#[test] +fn one_simple_block() { + assert_eq!( + Document::parse("abc").unwrap(), + TDocument { + source: TSpan { + data: "abc", + line: 1, + col: 1, + offset: 0 + }, + blocks: vec![TBlock::Simple(TSimpleBlock { + inlines: vec![TSpan { + data: "abc", + line: 1, + col: 1, + offset: 0, + },], + source: TSpan { + data: "abc", + line: 1, + col: 1, + offset: 0, + } + })], + } + ); +} + +#[test] +fn two_simple_blocks() { + assert_eq!( + Document::parse("abc\n\ndef").unwrap(), + TDocument { + source: TSpan { + data: "abc\n\ndef", + line: 1, + col: 1, + offset: 0 + }, + blocks: vec![ + TBlock::Simple(TSimpleBlock { + inlines: vec![TSpan { + data: "abc", + line: 1, + col: 1, + offset: 0, + },], + source: TSpan { + data: "abc\n", + line: 1, + col: 1, + offset: 0, + } + }), + TBlock::Simple(TSimpleBlock { + inlines: vec![TSpan { + data: "def", + line: 3, + col: 1, + offset: 5, + },], + source: TSpan { + data: "def", + line: 3, + col: 1, + offset: 5, + } + }), + ], + } + ); +} diff --git a/src/tests/document/mod.rs b/src/tests/document/mod.rs index 92f10f2..1624deb 100644 --- a/src/tests/document/mod.rs +++ b/src/tests/document/mod.rs @@ -1,116 +1,5 @@ -use pretty_assertions_sorted::assert_eq; - -use crate::{ - document::Document, - tests::fixtures::{ - blocks::{TBlock, TSimpleBlock}, - document::TDocument, - TSpan, - }, -}; - -#[test] -fn empty_source() { - assert_eq!( - Document::parse("").unwrap(), - TDocument { - source: TSpan { - data: "", - line: 1, - col: 1, - offset: 0 - }, - blocks: vec![], - } - ); -} - -#[test] -fn only_spaces() { - assert_eq!( - Document::parse(" ").unwrap(), - TDocument { - source: TSpan { - data: " ", - line: 1, - col: 1, - offset: 0 - }, - blocks: vec![], - } - ); -} - -#[test] -fn one_simple_block() { - assert_eq!( - Document::parse("abc").unwrap(), - TDocument { - source: TSpan { - data: "abc", - line: 1, - col: 1, - offset: 0 - }, - blocks: vec![TBlock::Simple(TSimpleBlock { - inlines: vec![TSpan { - data: "abc", - line: 1, - col: 1, - offset: 0, - },], - source: TSpan { - data: "abc", - line: 1, - col: 1, - offset: 0, - } - })], - } - ); -} - -#[test] -fn two_simple_blocks() { - assert_eq!( - Document::parse("abc\n\ndef").unwrap(), - TDocument { - source: TSpan { - data: "abc\n\ndef", - line: 1, - col: 1, - offset: 0 - }, - blocks: vec![ - TBlock::Simple(TSimpleBlock { - inlines: vec![TSpan { - data: "abc", - line: 1, - col: 1, - offset: 0, - },], - source: TSpan { - data: "abc\n", - line: 1, - col: 1, - offset: 0, - } - }), - TBlock::Simple(TSimpleBlock { - inlines: vec![TSpan { - data: "def", - line: 3, - col: 1, - offset: 5, - },], - source: TSpan { - data: "def", - line: 3, - col: 1, - offset: 5, - } - }), - ], - } - ); -} +// I understand the purpose behind this warning, but +// this module/submodule layout feels preferable in this +// circumstance. +#[allow(clippy::module_inception)] +mod document; diff --git a/src/tests/fixtures/document/tdocument.rs b/src/tests/fixtures/document/document.rs similarity index 100% rename from src/tests/fixtures/document/tdocument.rs rename to src/tests/fixtures/document/document.rs diff --git a/src/tests/fixtures/document/mod.rs b/src/tests/fixtures/document/mod.rs index e0b5898..c926b3a 100644 --- a/src/tests/fixtures/document/mod.rs +++ b/src/tests/fixtures/document/mod.rs @@ -1,2 +1,6 @@ -mod tdocument; -pub(crate) use tdocument::TDocument; +// I understand the purpose behind this warning, but +// this module/submodule layout feels preferable in this +// circumstance. +#[allow(clippy::module_inception)] +mod document; +pub(crate) use document::TDocument;