use crate::parse::{attributes::attributes, link::link_suffix};
use crate::structs::{ImageAlign, ImageHeader, InlineTag};
use nom::{
branch::alt,
bytes::complete::{escaped_transform, tag},
character::complete::{char, none_of},
combinator::{opt, value},
sequence::{delimited, tuple},
IResult,
};
pub fn image(input: &str) -> IResult<&str, InlineTag> {
let (rest, ((align, attributes, url, alt), opt_link)) = tuple((
delimited(
char('!'),
tuple((
opt(image_alignment),
opt(attributes),
image_url,
opt(delimited(char('('), image_alt, char(')'))),
)),
char('!'),
),
opt(link_suffix),
))(input)?;
let img = InlineTag::Image {
header: ImageHeader { attributes, align },
url,
alt,
};
Ok((
rest,
match opt_link {
Some(link) => InlineTag::Link {
attributes: None,
title: None,
url: link,
content: Box::new(img),
},
None => img,
},
))
}
fn image_alignment(input: &str) -> IResult<&str, ImageAlign> {
alt((
value(ImageAlign::Left, tag("<")),
value(ImageAlign::Right, tag(">")),
value(ImageAlign::Center, tag("=")),
))(input)
}
fn image_url(input: &str) -> IResult<&str, String> {
let (rest, url) = escaped_transform(
none_of("\\()! \r\n\t"),
'\\',
alt((
value("\\", tag("\\")),
value("(", tag("(")),
value(")", tag(")")),
value("!", tag("!")),
)),
)(input)?;
Ok((rest, String::from(url)))
}
fn image_alt(input: &str) -> IResult<&str, String> {
let (rest, alt) = escaped_transform(
none_of("\\()!"),
'\\',
alt((
value("\\", tag("\\")),
value("(", tag("(")),
value(")", tag(")")),
value("!", tag("!")),
)),
)(input)?;
Ok((rest, String::from(alt)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn image_basic() {
let input =
"!https://git.sr.ht/~autumnull/flatiron/blob/main/images/flatiron.png!";
let result = image(input);
assert_eq!(
result,
Ok((
"",
InlineTag::Image {
header: ImageHeader {
attributes: None,
align: None,
},
url: String::from(
"https://git.sr.ht/~autumnull/flatiron/blob/main/images/flatiron.png",
),
alt: None
}
))
);
}
#[test]
fn image_with_alt() {
let input =
"!https://git.sr.ht/~autumnull/flatiron/blob/main/images/flatiron.png(Flatiron logo)!";
let result = image(input);
assert_eq!(
result,
Ok((
"",
InlineTag::Image {
header: ImageHeader {attributes: None,
align: None,},
url: String::from(
"https://git.sr.ht/~autumnull/flatiron/blob/main/images/flatiron.png",
),
alt: Some(String::from("Flatiron logo")),
}
))
);
}
#[test]
fn image_with_parentheses() {
let input =
"!https://git.sr.ht/~autumnull/flatiron/blob/main/images/flatiron.png\\(parentheses\\)!";
let result = image(input);
assert_eq!(
result,
Ok((
"",
InlineTag::Image {
header: ImageHeader {
attributes: None,
align: None,
},
url: String::from(
"https://git.sr.ht/~autumnull/flatiron/blob/main/images/flatiron.png(parentheses)",
),
alt: None
}
))
);
}
#[test]
fn image_with_exclamation() {
let input =
"!https://git.sr.ht/~autumnull/flatiron/blob/main/images/flatiron.png\\!\\!!";
let result = image(input);
assert_eq!(
result,
Ok((
"",
InlineTag::Image {
header: ImageHeader {attributes: None,
align: None,},
url: String::from(
"https://git.sr.ht/~autumnull/flatiron/blob/main/images/flatiron.png!!",
),
alt: None,
}
))
);
}
#[test]
#[should_panic]
fn not_an_image() {
let input = "Stop! This is not an image, leave me alone!";
image(input).unwrap();
}
#[test]
fn image_link() {
let input = "!https://git.sr.ht/~autumnull/flatiron/blob/main/images/flatiron.png!:https://git.sr.ht/~autumnull/flatiron";
let result = image(input);
assert_eq!(
result,
Ok((
"",
InlineTag::Link {
attributes: None,
title: None,
url: String::from("https://git.sr.ht/~autumnull/flatiron"),
content: Box::new(InlineTag::Image {
header: ImageHeader {attributes: None,
align: None,},
url: String::from("https://git.sr.ht/~autumnull/flatiron/blob/main/images/flatiron.png"),
alt: None,
})
}
))
);
}
#[test]
fn image_link_parenthesized() {
let input = "!https://git.sr.ht/~autumnull/flatiron/blob/main/images/flatiron.png!:(https://git.sr.ht/~autumnull/flatiron)";
let result = image(input);
assert_eq!(
result,
Ok((
"",
InlineTag::Link {
attributes: None,
title: None,
url: String::from("https://git.sr.ht/~autumnull/flatiron"),
content: Box::new(InlineTag::Image {
header: ImageHeader {attributes: None,
align: None,},
url: String::from("https://git.sr.ht/~autumnull/flatiron/blob/main/images/flatiron.png"),
alt: None,
})
}
))
);
}
}