Skip to content

Commit

Permalink
Initial data model for inline elements (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
scouten authored Jan 8, 2024
1 parent 81f503b commit e366c79
Show file tree
Hide file tree
Showing 9 changed files with 306 additions and 0 deletions.
57 changes: 57 additions & 0 deletions src/inlines/inline.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use nom::{multi::many1, IResult};

use crate::{
primitives::{non_empty_line, trim_input_for_rem},
HasSpan, Span,
};

/// An inline element is a phrase (i.e., span of content) within a block element
/// or one of its attributes in an AsciiDoc document.
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum Inline<'a> {
/// Uninterpreted text (i.e., plain text) is text (character data) for which
/// all inline grammar rules fail to match.
Uninterpreted(Span<'a>),

/// A sequence of other inline blocks.
Sequence(Vec<Self>, Span<'a>),
}

impl<'a> Inline<'a> {
/// Parse a span (typically a line) of any type and return an `Inline` that
/// describes it.
#[allow(dead_code)]
pub(crate) fn parse(i: Span<'a>) -> IResult<Span, Self> {
// TEMPORARY: Naive approach ... everything is a plain span.
// Assuming for now that it's a line.

let (rem, span) = non_empty_line(i)?;
Ok((rem, Self::Uninterpreted(span)))
}

/// Parse a sequence of non-empty lines as a single `Inline` that
/// describes it.
#[allow(dead_code)]
pub(crate) fn parse_lines(i: Span<'a>) -> IResult<Span, Self> {
let (rem, first_line) = Self::parse(i)?;

if let Ok((rem2, mut more_inlines)) = many1(Self::parse)(rem) {
more_inlines.insert(0, first_line);

let source = trim_input_for_rem(i, rem2);
Ok((rem2, Self::Sequence(more_inlines, source)))
} else {
Ok((rem, first_line))
}
}
}

impl<'a> HasSpan<'a> for Inline<'a> {
fn span(&'a self) -> &'a Span<'a> {
match self {
Self::Uninterpreted(i) => i,
Self::Sequence(_, i) => i,
}
}
}
5 changes: 5 additions & 0 deletions src/inlines/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//! An inline element is a phrase (i.e., span of content) within a block element
//! or one of its attributes in an AsciiDoc document.

mod inline;
pub use inline::Inline;
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ pub use document::Document;
mod error;
pub use error::{Error, ParseResult};

pub mod inlines;

mod has_span;
pub use has_span::HasSpan;

Expand Down
45 changes: 45 additions & 0 deletions src/tests/fixtures/inlines/inline.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use crate::{inlines::Inline, tests::fixtures::span::TSpan};

#[derive(Debug, Eq, PartialEq)]
pub(crate) enum TInline {
Uninterpreted(TSpan),
Sequence(Vec<Self>, TSpan),
}

impl<'a> PartialEq<Inline<'a>> for TInline {
fn eq(&self, other: &Inline<'a>) -> bool {
tinline_eq(self, other)
}
}

impl<'a> PartialEq<TInline> for Inline<'a> {
fn eq(&self, other: &TInline) -> bool {
tinline_eq(other, self)
}
}

fn tinline_eq(tinline: &TInline, inline: &Inline) -> bool {
match tinline {
TInline::Uninterpreted(ref tspan) => match inline {
Inline::Uninterpreted(ref span) => tspan == span,
_ => false,
},

TInline::Sequence(ref tinlines, ref tspan) => match inline {
Inline::Sequence(ref inlines, ref span) => {
if tinlines.len() != inlines.len() {
return false;
}

for (tinline, inline) in tinlines.iter().zip(inlines.iter()) {
if tinline != inline {
return false;
}
}

tspan == span
}
_ => false,
},
}
}
2 changes: 2 additions & 0 deletions src/tests/fixtures/inlines/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod inline;
pub(crate) use inline::TInline;
1 change: 1 addition & 0 deletions src/tests/fixtures/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::{env, path::PathBuf};

pub(crate) mod blocks;
pub(crate) mod document;
pub(crate) mod inlines;

mod span;
pub(crate) use span::TSpan;
Expand Down
192 changes: 192 additions & 0 deletions src/tests/inlines/inline.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
mod uninterpreted {
use nom::{
error::{Error, ErrorKind},
Err,
};
use pretty_assertions_sorted::assert_eq;

use crate::{
inlines::Inline,
tests::fixtures::{inlines::TInline, TSpan},
HasSpan, Span,
};

#[test]
fn impl_clone() {
// Silly test to mark the #[derive(...)] line as covered.
let (_, b1) = Inline::parse(Span::new("abc", true)).unwrap();
let b2 = b1.clone();
assert_eq!(b1, b2);
}

#[test]
fn empty_source() {
let expected_err = Err::Error(Error::new(Span::new("", true), ErrorKind::TakeTill1));

let actual_err = Inline::parse(Span::new("", true)).unwrap_err();

assert_eq!(expected_err, actual_err);
}

#[test]
fn only_spaces() {
let expected_err = Err::Error(Error::new(Span::new(" ", true), ErrorKind::TakeTill1));

let actual_err = Inline::parse(Span::new(" ", true)).unwrap_err();

assert_eq!(expected_err, actual_err);
}

#[test]
fn simple_line() {
let (rem, inline) = Inline::parse(Span::new("abc", true)).unwrap();

assert_eq!(
rem,
TSpan {
data: "",
line: 1,
col: 4,
offset: 3
}
);

assert_eq!(
inline,
TInline::Uninterpreted(TSpan {
data: "abc",
line: 1,
col: 1,
offset: 0
})
);

assert_eq!(
inline.span(),
TSpan {
data: "abc",
line: 1,
col: 1,
offset: 0,
}
);
}
}

mod parse_lines {
use nom::{
error::{Error, ErrorKind},
Err,
};
use pretty_assertions_sorted::assert_eq;

use crate::{
inlines::Inline,
tests::fixtures::{inlines::TInline, TSpan},
HasSpan, Span,
};

#[test]
fn empty_source() {
let expected_err = Err::Error(Error::new(Span::new("", true), ErrorKind::TakeTill1));

let actual_err = Inline::parse_lines(Span::new("", true)).unwrap_err();

assert_eq!(expected_err, actual_err);
}

#[test]
fn only_spaces() {
let expected_err = Err::Error(Error::new(Span::new(" ", true), ErrorKind::TakeTill1));

let actual_err = Inline::parse_lines(Span::new(" ", true)).unwrap_err();

assert_eq!(expected_err, actual_err);
}

#[test]
fn simple_line() {
let (rem, inline) = Inline::parse_lines(Span::new("abc", true)).unwrap();

assert_eq!(
rem,
TSpan {
data: "",
line: 1,
col: 4,
offset: 3
}
);

assert_eq!(
inline,
TInline::Uninterpreted(TSpan {
data: "abc",
line: 1,
col: 1,
offset: 0
})
);

assert_eq!(
inline.span(),
TSpan {
data: "abc",
line: 1,
col: 1,
offset: 0,
}
);
}

#[test]
fn two_lines() {
let (rem, inline) = Inline::parse_lines(Span::new("abc\ndef", true)).unwrap();

assert_eq!(
rem,
TSpan {
data: "",
line: 2,
col: 4,
offset: 7
}
);

assert_eq!(
inline,
TInline::Sequence(
vec!(
TInline::Uninterpreted(TSpan {
data: "abc",
line: 1,
col: 1,
offset: 0
}),
TInline::Uninterpreted(TSpan {
data: "def",
line: 2,
col: 1,
offset: 4
})
),
TSpan {
data: "abc\ndef",
line: 1,
col: 1,
offset: 0,
}
)
);

assert_eq!(
inline.span(),
TSpan {
data: "abc\ndef",
line: 1,
col: 1,
offset: 0,
}
);
}
}
1 change: 1 addition & 0 deletions src/tests/inlines/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod inline;
1 change: 1 addition & 0 deletions src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ mod blocks;
mod document;
mod error;
pub(crate) mod fixtures;
mod inlines;
mod primitives;
mod strings;

0 comments on commit e366c79

Please sign in to comment.