hyaenidae/content/src/email.rs
asonix 0e1552eeaa Content: Use own bbcode impl
This gives us more control over things, like
automatic 'linkifying', and the ability to add
custom user tagging logic
2021-01-31 13:50:34 -06:00

128 lines
2.9 KiB
Rust

use crate::url::domain;
use combine::{
choice, many, many1,
parser::char::{alpha_num, char as parsechar},
Parser, Stream,
};
fn unquoted<Input>() -> impl Parser<Input, Output = char>
where
Input: Stream<Token = char>,
{
choice((
alpha_num(),
parsechar('!'),
parsechar('#'),
parsechar('$'),
parsechar('%'),
parsechar('&'),
parsechar('\''),
parsechar('*'),
parsechar('+'),
parsechar('-'),
parsechar('/'),
parsechar('='),
parsechar('?'),
parsechar('^'),
parsechar('`'),
parsechar('{'),
parsechar('|'),
parsechar('}'),
parsechar('~'),
))
}
fn unquoted_middle_segment<Input>() -> impl Parser<Input, Output = String>
where
Input: Stream<Token = char>,
{
parsechar('.')
.and(many1(unquoted()))
.map(|(c, s): (_, String)| {
let mut string = String::new();
string.push(c);
string += &s;
string
})
}
fn unquoted_full<Input>() -> impl Parser<Input, Output = String>
where
Input: Stream<Token = char>,
{
many1(unquoted())
.and(many(unquoted_middle_segment()))
.map(|(s1, s2): (String, String)| s1 + &s2)
}
pub(crate) fn email<Input>() -> impl Parser<Input, Output = String>
where
Input: Stream<Token = char>,
{
unquoted_full()
.skip(parsechar('@'))
.and(domain().map(|d| d.0))
.map(|(local, domain)| format!("{}@{}", local, domain))
}
#[cfg(test)]
mod tests {
use super::*;
use combine::EasyParser;
#[test]
fn unquoted_parses_chars() {
for c in &['a', 'b', '$', '#'] {
let s = c.to_string();
let (_, rest) = unquoted().easy_parse(s.as_str()).unwrap();
assert_eq!(rest, "");
}
}
#[test]
fn unquoted_middle_segment_parses_dots() {
let (_, rest) = unquoted_middle_segment().easy_parse(".one").unwrap();
assert_eq!(rest, "");
}
#[test]
fn unquoted_full_parses_dots() {
let (_, rest) = unquoted_full().easy_parse("one.two.three.four").unwrap();
assert_eq!(rest, "");
}
#[test]
fn parses_basic_email() {
let (_, rest) = email().easy_parse("a@b").unwrap();
assert_eq!(rest, "");
}
#[test]
fn doesnt_parse_invalid_email() {
assert!(email().easy_parse("@a@b").is_err());
}
#[test]
fn parses_longer_email() {
let (_, rest) = email()
.easy_parse("one.two.three.four@sub.domain.tld")
.unwrap();
assert_eq!(rest, "");
}
#[test]
fn doesnt_parse_double_dot() {
assert!(email().easy_parse("bad..email@tld").is_err());
}
#[test]
fn doesnt_parse_dot_local() {
assert!(email().easy_parse(".local@tld").is_err());
}
#[test]
fn doesnt_parse_end_dot_local() {
assert!(email().easy_parse("local.@tld").is_err());
}
}