use crate::url::domain; use combine::{ choice, many, many1, parser::char::{alpha_num, char as parsechar}, Parser, Stream, }; fn unquoted() -> impl Parser where Input: Stream, { 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() -> impl Parser where Input: Stream, { parsechar('.') .and(many1(unquoted())) .map(|(c, s): (_, String)| { let mut string = String::new(); string.push(c); string += &s; string }) } fn unquoted_full() -> impl Parser where Input: Stream, { many1(unquoted()) .and(many(unquoted_middle_segment())) .map(|(s1, s2): (String, String)| s1 + &s2) } pub(crate) fn email() -> impl Parser where Input: Stream, { 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()); } }