use nom::branch::alt; use nom::bytes::complete::tag; use nom::character::complete::alphanumeric1; use nom::character::complete::line_ending; use nom::character::complete::space1; use nom::combinator::map; use nom::combinator::not; use nom::combinator::recognize; use nom::multi::many_till; use super::bold::bold; use super::error::Res; use super::link::link; use super::parser_with_context::parser_with_context; use super::token::BlankLine; use super::token::LineBreak; use super::token::Space; use super::token::Span; use super::token::Symbol; use super::token::TextElement; use super::Context; pub fn line_break(input: &str) -> Res<&str, LineBreak> { map(line_ending, |s: &str| LineBreak { contents: s })(input) } fn space(input: &str) -> Res<&str, Space> { map(space1, |s: &str| Space { contents: s })(input) } fn span(input: &str) -> Res<&str, Span> { map(alphanumeric1, |s: &str| Span { contents: s })(input) } pub fn symbol(symbol_tag: &'static str) -> impl for<'a> Fn(&'a str) -> Res<&'a str, Symbol<'a>> { move |i: &str| map(tag(symbol_tag), |s: &str| Symbol { contents: s })(i) } /// A line containing only whitespace and then a line break /// /// It is up to the caller to ensure this is called at the start of a line. pub fn blank_line(input: &str) -> Res<&str, BlankLine> { map( recognize(many_till( map(space, TextElement::Space), map(line_break, TextElement::LineBreak), )), |contents| BlankLine { contents }, )(input) } pub fn text_element<'s, 'r>(context: Context<'r, 's>, i: &'s str) -> Res<&'s str, TextElement<'s>> { not(|i| context.check_exit_matcher(i))(i)?; let bold_matcher = parser_with_context!(bold)(&context); let link_matcher = parser_with_context!(link)(&context); alt(( map(bold_matcher, TextElement::Bold), map(link_matcher, TextElement::Link), map(span, TextElement::Span), map(symbol("*"), TextElement::Symbol), map(symbol("["), TextElement::Symbol), map(symbol("]"), TextElement::Symbol), map(space, TextElement::Space), map(line_break, TextElement::LineBreak), ))(i) }