use super::Span;
use nom::{bytes::complete::take, IResult};
pub fn strong(input: Span) -> IResult<Span, Span> {
log::debug!("Parsing strong emphasis at: {:?}", input.fragment());
if let Ok(result) = strong_with_delimiter(input, '*') {
return Ok(result);
}
strong_with_delimiter(input, '_')
}
fn strong_with_delimiter(input: Span, delimiter: char) -> IResult<Span, Span> {
let content_str = input.fragment();
if content_str.len() < 2 {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Tag,
)));
}
if !content_str.starts_with(&format!("{}{}", delimiter, delimiter)) {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Tag,
)));
}
let (after_opening, _) = take(2usize)(input)?;
let remaining_str = after_opening.fragment();
if remaining_str.is_empty() {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::TakeUntil,
)));
}
let mut pos = 0;
while pos < remaining_str.len() {
if remaining_str.as_bytes()[pos] == b'`' {
pos += 1;
while pos < remaining_str.len() && remaining_str.as_bytes()[pos] != b'`' {
pos += 1;
}
if pos < remaining_str.len() {
pos += 1; }
continue;
}
if pos + 1 < remaining_str.len()
&& remaining_str.as_bytes()[pos] == delimiter as u8
&& remaining_str.as_bytes()[pos + 1] == delimiter as u8
{
if pos > 0 {
let (after_content, content) = take(pos)(after_opening)?;
let (remaining, _closing) = take(2usize)(after_content)?;
return Ok((remaining, 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_strong_asterisk() {
let input = Span::new("**strong** text");
let result = strong(input);
assert!(result.is_ok());
let (rest, content) = result.unwrap();
assert_eq!(*content.fragment(), "strong");
assert_eq!(*rest.fragment(), " text");
}
#[test]
fn smoke_test_strong_underscore() {
let input = Span::new("__strong__ text");
let result = strong(input);
assert!(result.is_ok());
let (_, content) = result.unwrap();
assert_eq!(*content.fragment(), "strong");
}
#[test]
fn smoke_test_strong_no_closing() {
let input = Span::new("**no closing");
let result = strong(input);
assert!(result.is_err());
}
#[test]
fn smoke_test_strong_empty_content() {
let input = Span::new("****");
let result = strong(input);
assert!(result.is_err()); }
#[test]
fn smoke_test_strong_with_code_span() {
let input = Span::new("**text with `code`** more");
let result = strong(input);
assert!(result.is_ok());
}
#[test]
fn smoke_test_strong_single_char() {
let input = Span::new("**a** text");
let result = strong(input);
assert!(result.is_ok());
let (_, content) = result.unwrap();
assert_eq!(*content.fragment(), "a");
}
#[test]
fn smoke_test_strong_not_emphasis() {
let input = Span::new("*emphasis*");
let result = strong(input);
assert!(result.is_err()); }
}