asonix
0e1552eeaa
This gives us more control over things, like automatic 'linkifying', and the ability to add custom user tagging logic
128 lines
2.9 KiB
Rust
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());
|
|
}
|
|
}
|