use crate::structs::InlineTag;
use nom::{
branch::alt,
bytes::complete::tag,
character::complete::{char, digit1, satisfy},
combinator::{eof, map, opt, value},
sequence::tuple,
IResult,
};
pub fn punctuation(input: &str) -> IResult<&str, InlineTag> {
let (rest, s) = alt((
apostrophe,
apostrophe2,
single_closing,
double_closing,
dimension_sign,
map(
alt((
value("‘", tag("'")),
value("“", tag("\"")),
value("…", tag("...")),
value("—", tag("--")),
value(" – ", tag(" - ")),
value("™", tag("(TM)")),
value("®", tag("(R)")),
value("©", tag("(C)")),
)),
|s| String::from(s),
),
))(input)?;
Ok((rest, InlineTag::Plaintext(s)))
}
fn apostrophe(input: &str) -> IResult<&str, String> {
let (rest, (c0, apos, c1)) =
tuple((alphanumeric, value('’', char('\'')), alphanumeric))(input)?;
Ok((rest, format!("{}{}{}", c0, apos, c1)))
}
fn apostrophe2(input: &str) -> IResult<&str, String> {
let (rest, (space, apos, num, alphanum, end)) = tuple((
whitespace,
value('’', char('\'')),
digit1,
opt(alphanumeric),
satisfy(|c| !(c.is_alphanumeric() || c == '\'')),
))(input)?;
Ok((
rest,
format!(
"{}{}{}{}{}",
space,
apos,
num,
match alphanum {
Some(c) => String::from(c),
None => String::new(),
},
end
),
))
}
fn single_closing(input: &str) -> IResult<&str, String> {
let (rest, (non_space, apos, end)) = tuple((
non_whitespace,
value('’', char('\'')),
alt((
value(String::new(), eof),
map(non_alphanumeric, |c| String::from(c)),
)),
))(input)?;
Ok((rest, format!("{}{}{}", non_space, apos, end)))
}
fn double_closing(input: &str) -> IResult<&str, String> {
let (rest, (non_space, apos, end)) = tuple((
non_whitespace,
value('”', char('"')),
alt((
value(String::new(), eof),
map(non_alphanumeric, |c| String::from(c)),
)),
))(input)?;
Ok((rest, format!("{}{}{}", non_space, apos, end)))
}
fn alphanumeric(input: &str) -> IResult<&str, char> {
satisfy(|c| c.is_alphanumeric())(input)
}
fn non_alphanumeric(input: &str) -> IResult<&str, char> {
satisfy(|c| !c.is_alphanumeric())(input)
}
fn whitespace(input: &str) -> IResult<&str, char> {
satisfy(|c| c.is_whitespace())(input)
}
fn non_whitespace(input: &str) -> IResult<&str, char> {
satisfy(|c| !c.is_whitespace())(input)
}
fn dimension_sign(input: &str) -> IResult<&str, String> {
let (rest, (num0, space0, x, space1, num1)) = tuple((
digit1,
alt((tag(" "), tag(""))),
value('×', char('x')),
alt((tag(" "), tag(""))),
digit1,
))(input)?;
Ok((rest, format!("{}{}{}{}{}", num0, space0, x, space1, num1)))
}