use nom::{IResult, Input};
use super::Span;
pub fn strong_emphasis(input: Span) -> IResult<Span, Span> {
if let Ok(result) = strong_emphasis_with_delimiter(input, '*') {
return Ok(result);
}
strong_emphasis_with_delimiter(input, '_')
}
fn strong_emphasis_with_delimiter(input: Span, delimiter: char) -> IResult<Span, Span> {
let s = input.fragment();
if !s.starts_with(&format!("{d}{d}{d}", d = delimiter)) {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Tag,
)));
}
if s.chars().nth(3) == Some(delimiter) {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Tag,
)));
}
let after_opening = input.take_from(3);
let remaining = after_opening.fragment();
if remaining.is_empty() {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::TakeUntil,
)));
}
let mut pos = 0;
while pos < remaining.len() {
if remaining.as_bytes()[pos] == b'`' {
pos += 1;
while pos < remaining.len() && remaining.as_bytes()[pos] != b'`' {
pos += 1;
}
if pos < remaining.len() {
pos += 1;
}
continue;
}
if pos + 2 < remaining.len()
&& remaining.as_bytes()[pos] == delimiter as u8
&& remaining.as_bytes()[pos + 1] == delimiter as u8
&& remaining.as_bytes()[pos + 2] == delimiter as u8
{
if pos == 0 {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::TakeUntil,
)));
}
let content = after_opening.take(pos);
let rest = after_opening.take_from(pos + 3);
return Ok((rest, content));
}
pos += 1;
}
Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::TakeUntil,
)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn smoke_test_triple_asterisk() {
let input = Span::new("***bold and italic*** rest");
let (rest, content) = strong_emphasis(input).unwrap();
assert_eq!(*content.fragment(), "bold and italic");
assert_eq!(*rest.fragment(), " rest");
}
#[test]
fn smoke_test_triple_underscore() {
let input = Span::new("___bold and italic___");
let (rest, content) = strong_emphasis(input).unwrap();
assert_eq!(*content.fragment(), "bold and italic");
assert_eq!(*rest.fragment(), "");
}
#[test]
fn smoke_test_reject_four_delimiters() {
let input = Span::new("****nope****");
assert!(strong_emphasis(input).is_err());
}
}