use super::Span;
use nom::{IResult, Input};
pub fn emphasis(input: Span) -> IResult<Span, Span> {
log::debug!("Parsing emphasis at: {:?}", input.fragment());
if let Ok(result) = emphasis_with_delimiter(input, '*') {
return Ok(result);
}
emphasis_with_delimiter(input, '_')
}
fn emphasis_with_delimiter(input: Span, delimiter: char) -> IResult<Span, Span> {
let content_str = input.fragment();
if !content_str.starts_with(delimiter) {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Tag,
)));
}
if content_str.len() > 1 && content_str.chars().nth(1) == Some(delimiter) {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Tag,
)));
}
let after_opening = input.take_from(1);
let remaining_str = after_opening.fragment();
let mut pos = 0;
if remaining_str.is_empty() {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::TakeUntil,
)));
}
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 remaining_str.as_bytes()[pos] == delimiter as u8 {
if pos + 1 < remaining_str.len() && remaining_str.as_bytes()[pos + 1] == delimiter as u8
{
pos += 2;
continue;
}
if pos > 0 {
let content = after_opening.take(pos);
let remaining = after_opening.take_from(pos + 1);
log::debug!("Emphasis content: {:?}", content.fragment());
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_emphasis_asterisk() {
let input = Span::new("*emphasized* text");
let result = emphasis(input);
assert!(result.is_ok());
let (rest, content) = result.unwrap();
assert_eq!(*content.fragment(), "emphasized");
assert_eq!(*rest.fragment(), " text");
}
#[test]
fn smoke_test_emphasis_underscore() {
let input = Span::new("_emphasized_ text");
let result = emphasis(input);
assert!(result.is_ok());
let (_, content) = result.unwrap();
assert_eq!(*content.fragment(), "emphasized");
}
#[test]
fn smoke_test_emphasis_no_closing() {
let input = Span::new("*no closing");
let result = emphasis(input);
assert!(result.is_err());
}
#[test]
fn smoke_test_emphasis_empty_content() {
let input = Span::new("**");
let result = emphasis(input);
assert!(result.is_err()); }
#[test]
fn smoke_test_emphasis_with_code_span() {
let input = Span::new("*text with `code`* more");
let result = emphasis(input);
assert!(result.is_ok());
}
#[test]
fn smoke_test_emphasis_not_strong() {
let input = Span::new("**strong**");
let result = emphasis(input);
assert!(result.is_err()); }
#[test]
fn smoke_test_emphasis_single_char() {
let input = Span::new("*a* text");
let result = emphasis(input);
assert!(result.is_ok());
let (_, content) = result.unwrap();
assert_eq!(*content.fragment(), "a");
}
}