#![deny(missing_docs)]
use nom::branch::alt;
use nom::bytes::complete::{is_not, tag, take_until};
use nom::multi::{fold_many0, many0};
use nom::sequence::{delimited, pair, preceded, terminated, tuple};
use nom::Parser;
pub struct Parens;
impl<'a> Parser<&'a str, &'a str, ()> for Parens {
fn parse(&mut self, input: &'a str) -> Result<(&'a str, &'a str), nom::Err<()>> {
alt((NonEmptyParensPair, EmptyParensPair))(input)
}
}
pub struct LeftParens;
impl<'a> Parser<&'a str, &'a str, ()> for LeftParens {
fn parse(&mut self, input: &'a str) -> Result<(&'a str, &'a str), nom::Err<()>> {
tag("(")(input)
}
}
pub struct RightParens;
impl<'a> Parser<&'a str, &'a str, ()> for RightParens {
fn parse(&mut self, input: &'a str) -> Result<(&'a str, &'a str), nom::Err<()>> {
tag(")")(input)
}
}
pub struct NonEmptyParensPair;
impl<'a> Parser<&'a str, &'a str, ()> for NonEmptyParensPair {
fn parse(&mut self, input: &'a str) -> Result<(&'a str, &'a str), nom::Err<()>> {
delimited(LeftParens, is_not(")"), RightParens)(input)
}
}
pub struct EmptyParensPair;
impl<'a> Parser<&'a str, &'a str, ()> for EmptyParensPair {
fn parse(&mut self, input: &'a str) -> Result<(&'a str, &'a str), nom::Err<()>> {
terminated(LeftParens, RightParens)(input)
}
}
pub struct LeftMarkdownImageBracket;
impl<'a> Parser<&'a str, &'a str, ()> for LeftMarkdownImageBracket {
fn parse(&mut self, input: &'a str) -> Result<(&'a str, &'a str), nom::Err<()>> {
tag("![")(input)
}
}
pub struct EmptyMarkdownImageBracketPair;
impl<'a> Parser<&'a str, &'a str, ()> for EmptyMarkdownImageBracketPair {
fn parse(&mut self, input: &'a str) -> Result<(&'a str, &'a str), nom::Err<()>> {
terminated(LeftMarkdownImageBracket, RightBracket)(input)
}
}
pub struct NonEmptyMarkdownImageBracketPair;
impl<'a> Parser<&'a str, &'a str, ()> for NonEmptyMarkdownImageBracketPair {
fn parse(&mut self, input: &'a str) -> Result<(&'a str, &'a str), nom::Err<()>> {
delimited(LeftMarkdownImageBracket, is_not("]"), RightBracket)(input)
}
}
pub struct LeftBracket;
impl<'a> Parser<&'a str, &'a str, ()> for LeftBracket {
fn parse(&mut self, input: &'a str) -> Result<(&'a str, &'a str), nom::Err<()>> {
tag("[")(input)
}
}
pub struct RightBracket;
impl<'a> Parser<&'a str, &'a str, ()> for RightBracket {
fn parse(&mut self, input: &'a str) -> Result<(&'a str, &'a str), nom::Err<()>> {
tag("]")(input)
}
}
pub struct NonEmptyBracketPair;
impl<'a> Parser<&'a str, &'a str, ()> for NonEmptyBracketPair {
fn parse(&mut self, input: &'a str) -> Result<(&'a str, &'a str), nom::Err<()>> {
delimited(LeftBracket, is_not("]"), RightBracket)(input)
}
}
pub struct EmptyBracketPair;
impl<'a> Parser<&'a str, &'a str, ()> for EmptyBracketPair {
fn parse(&mut self, input: &'a str) -> Result<(&'a str, &'a str), nom::Err<()>> {
terminated(LeftBracket, RightBracket)(input)
}
}
pub struct Brackets;
impl<'a> Parser<&'a str, &'a str, ()> for Brackets {
fn parse(&mut self, input: &'a str) -> Result<(&'a str, &'a str), nom::Err<()>> {
alt((NonEmptyBracketPair, EmptyBracketPair))(input)
}
}
pub struct MarkdownImageBrackets;
impl<'a> Parser<&'a str, &'a str, ()> for MarkdownImageBrackets {
fn parse(&mut self, input: &'a str) -> Result<(&'a str, &'a str), nom::Err<()>> {
alt((
NonEmptyMarkdownImageBracketPair,
EmptyMarkdownImageBracketPair,
))(input)
}
}
pub struct MarkdownUrls;
pub type MarkdownUrlsResults<'a> =
Result<(&'a str, Vec<(&'a str, (&'a str, &'a str))>), nom::Err<()>>;
impl<'a> Parser<&'a str, Vec<(&'a str, (&'a str, &'a str))>, ()> for MarkdownUrls {
fn parse(&mut self, input: &'a str) -> MarkdownUrlsResults<'a> {
fold_many0(
pair(take_until("["), MarkdownUrl),
Vec::new,
|mut acc: Vec<_>, item| {
if !item.0.ends_with('!') {
acc.push(item);
}
acc
},
)(input)
}
}
pub struct MarkdownUrl;
impl<'a> Parser<&'a str, (&'a str, &'a str), ()> for MarkdownUrl {
fn parse(&mut self, input: &'a str) -> Result<(&'a str, (&'a str, &'a str)), nom::Err<()>> {
tuple((Brackets, Parens))(input)
}
}
pub struct MarkdownImages;
pub type MarkdownImagesResults<'a> = Result<(&'a str, Vec<(&'a str, &'a str)>), nom::Err<()>>;
impl<'a> Parser<&'a str, Vec<(&'a str, &'a str)>, ()> for MarkdownImages {
fn parse(&mut self, input: &'a str) -> MarkdownImagesResults<'a> {
many0(preceded(
take_until("!["),
tuple((MarkdownImageBrackets, Parens)),
))(input)
}
}
pub struct MarkdownImage;
impl<'a> Parser<&'a str, (&'a str, &'a str), ()> for MarkdownImage {
fn parse(&mut self, input: &'a str) -> Result<(&'a str, (&'a str, &'a str)), nom::Err<()>> {
tuple((MarkdownImageBrackets, Parens))(input)
}
}
pub struct MarkdownImageAltText;
impl<'a> Parser<&'a str, Vec<&'a str>, ()> for MarkdownImageAltText {
fn parse(&mut self, input: &'a str) -> Result<(&'a str, Vec<&'a str>), nom::Err<()>> {
MarkdownImage.parse(input).map(|v| {
let image = v.1;
let words = image.0.split_ascii_whitespace().collect();
(image.1, words)
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use nom::{
bytes::complete::is_not,
combinator::recognize,
sequence::{delimited, terminated},
};
#[test]
fn recognize_markdown_image_alt_text_sentence() -> anyhow::Result<()> {
let input = "";
let token = MarkdownImageAltText.parse(input)?;
assert_eq!(token.0, "https://www.google.com");
assert_eq!(
token.1,
vec![
"I",
"am",
"a",
"great",
"description.",
"Thanks",
"for",
"reading",
"me!"
]
);
assert_eq!(token.1.len(), 9);
Ok(())
}
#[test]
fn recognize_markdown_image_alt_text_words() -> anyhow::Result<()> {
let input = "";
let token = MarkdownImageAltText.parse(input)?;
assert_eq!(token.1, vec!["word", "word", "word"]);
Ok(())
}
#[test]
fn recognize_markdown_image() -> anyhow::Result<()> {
let input = "";
let token = recognize(MarkdownImage)(input)?;
assert_eq!(token, ("", ""));
let input = "";
let token = recognize(MarkdownImage)(input)?;
assert_eq!(token, ("", ""));
Ok(())
}
#[test]
fn recognize_markdown_url_not_image() -> anyhow::Result<()> {
let input = "here's some other stuff  here's [some anchor](please find me!) some more stuff";
let token = MarkdownUrls.parse(input)?;
let count = token.1.len();
assert_eq!(count, 1);
Ok(())
}
#[test]
fn recognize_markdown_url() -> anyhow::Result<()> {
let input = "[image](abcd)";
let token = recognize(MarkdownUrl)(input)?;
assert_eq!(token, ("", "[image](abcd)"));
Ok(())
}
#[test]
fn recognize_non_empty_markdown_image_brackets() -> anyhow::Result<()> {
let input = "![abcd]";
let token = recognize(NonEmptyMarkdownImageBracketPair)(input)?;
assert_eq!(token, ("", "![abcd]"));
Ok(())
}
#[test]
fn recognize_non_empty_parens() -> anyhow::Result<()> {
let input = "(abcd)";
let token = recognize(NonEmptyParensPair)(input)?;
assert_eq!(token, ("", "(abcd)"));
Ok(())
}
#[test]
fn recognize_empty_value() -> anyhow::Result<()> {
let input = "[]asdf[]";
let token = recognize(EmptyBracketPair)(input)?;
assert_eq!(token, ("asdf[]", "[]"));
Ok(())
}
#[test]
fn empty_value() -> anyhow::Result<()> {
let input = "[]";
let token = terminated(LeftBracket, RightBracket)(input)?;
assert_eq!(token, ("", "["));
Ok(())
}
#[test]
fn bracketed_value() -> anyhow::Result<()> {
let input = "[abc]";
let token = delimited(LeftBracket, is_not("]"), RightBracket)(input)?;
assert_eq!(token, ("", "abc"));
Ok(())
}
}