use nom::{branch::alt, character::complete::space0, multi::fold_many_m_n, IResult};
use nom_supreme::{error::ErrorTree, tag::complete::tag};
use std::fmt;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct ThematicBreak {
pub char_count: u8,
pub break_char: char,
}
impl fmt::Display for ThematicBreak {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
self.break_char.to_string().repeat(self.char_count.into())
)
}
}
fn optionally_surrounded_by_spaces<'a, F: 'a, O, E: nom::error::ParseError<&'a str>>(
inner: F,
) -> impl FnMut(&'a str) -> IResult<&'a str, O, E>
where
F: Fn(&'a str) -> IResult<&'a str, O, E>,
{
nom::sequence::delimited(space0, inner, space0)
}
pub fn thematic_break(input: &str) -> IResult<&str, ThematicBreak, ErrorTree<&str>> {
let (input, _) = fold_many_m_n(0, 3, tag(" "), 0, |acc: u8, _| acc + 1)(input)?;
let (input, c) = alt((
nom::character::complete::char('-'),
nom::character::complete::char('*'),
nom::character::complete::char('_'),
))(input)?;
let (input, num_break_chars) = fold_many_m_n(
2,
1000,
optionally_surrounded_by_spaces(nom::character::complete::char(c)),
0,
|acc: u8, _| acc + 1,
)(input)?;
Ok((
input,
ThematicBreak {
char_count: num_break_chars + 1,
break_char: c,
},
))
}
#[cfg(test)]
mod tests {
use super::*;
use nom::{error::ErrorKind, Err::Error};
#[test]
fn parse_thematic_break_dash() {
assert_eq!(
thematic_break("---").unwrap(),
(
"",
ThematicBreak {
char_count: 3,
break_char: '-'
}
)
);
}
#[test]
fn parse_thematic_break_star() {
assert_eq!(
thematic_break("***").unwrap(),
(
"",
ThematicBreak {
char_count: 3,
break_char: '*'
}
)
);
}
#[test]
fn parse_thematic_break_underscore() {
assert_eq!(
thematic_break("___").unwrap(),
(
"",
ThematicBreak {
char_count: 3,
break_char: '_'
}
)
);
}
#[test]
fn parse_thematic_break_spaces() {
assert_eq!(
thematic_break(" - - - ").unwrap(),
(
"",
ThematicBreak {
char_count: 3,
break_char: '-'
}
)
);
}
}