use nom::character::is_alphanumeric; use nom::{do_parse, named, tag, take_until, take_while, IResult}; use std::borrow::Cow; use std::fmt; use crate::{Parse, ParseError}; #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] pub struct Email<'a> { #[cfg_attr(feature = "typed-builder", builder(setter(into)))] pub user: Cow<'a, str>, #[cfg_attr(feature = "typed-builder", builder(setter(into)))] pub domain: Cow<'a, str>, } impl<'a> fmt::Display for Email<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}@{}", self.user, self.domain) } } impl<'a> Parse<'a> for Email<'a> { fn parse(input: &str) -> IResult<&str, Email, ParseError> { parse_email(input).map_err(|c| match c { nom::Err::Error(err) => nom::Err::Error(crate::parse::to_parse_error(input, err)), nom::Err::Failure(err) => nom::Err::Failure(crate::parse::to_parse_error(input, err)), nom::Err::Incomplete(n) => nom::Err::Incomplete(n), }) } } named!(parse_email<&str, Email>, do_parse!( user: take_until!("@") >> tag!("@") >> domain: take_while!(|x| is_alphanumeric(x as u8) || "_-.".contains(x)) >> (Email { user: user.into(), domain: domain.into() }) ));