/rust/registry/src/index.crates.io-1949cf8c6b5b557f/addr-0.15.6/src/email.rs
Line | Count | Source |
1 | | //! Email address types |
2 | | |
3 | | use crate::domain::Name; |
4 | | use crate::error::{Kind, Result}; |
5 | | use crate::matcher; |
6 | | #[cfg(feature = "net")] |
7 | | #[cfg(not(feature = "std"))] |
8 | | use crate::net::IpAddr; |
9 | | use core::fmt; |
10 | | #[cfg(not(any(feature = "net", feature = "std")))] |
11 | | use core::str::FromStr; |
12 | | use psl_types::List; |
13 | | #[cfg(feature = "std")] |
14 | | use std::net::IpAddr; |
15 | | |
16 | | /// Holds information about a particular email address |
17 | | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] |
18 | | pub struct Address<'a> { |
19 | | full: &'a str, |
20 | | at_sign: usize, |
21 | | host: Host<'a>, |
22 | | } |
23 | | |
24 | | impl<'a> Address<'a> { |
25 | 0 | pub(crate) fn parse<T: List + ?Sized>(list: &T, address: &'a str) -> Result<Address<'a>> { |
26 | 0 | if address.chars().count() > 254 { |
27 | 0 | return Err(Kind::EmailTooLong); |
28 | 0 | } |
29 | 0 | let at_sign = address.rfind('@').ok_or(Kind::NoAtSign)?; |
30 | 0 | let local = address.get(..at_sign).ok_or(Kind::NoUserPart)?; |
31 | 0 | matcher::is_email_local(local)?; |
32 | 0 | let rest = address.get(at_sign + 1..).ok_or(Kind::NoHostPart)?; |
33 | 0 | let host = Host::parse(list, rest)?; |
34 | 0 | Ok(Self { |
35 | 0 | host, |
36 | 0 | at_sign, |
37 | 0 | full: address, |
38 | 0 | }) |
39 | 0 | } |
40 | | |
41 | | /// The full email address as a `str` |
42 | 0 | pub const fn as_str(&self) -> &'a str { |
43 | 0 | self.full |
44 | 0 | } |
45 | | |
46 | | /// The host part of the email address |
47 | 0 | pub const fn host(&self) -> Host<'a> { |
48 | 0 | self.host |
49 | 0 | } |
50 | | |
51 | | /// The user (local) part of the email address |
52 | 0 | pub fn user(&self) -> &'a str { |
53 | 0 | &self.full[..self.at_sign] |
54 | 0 | } |
55 | | } |
56 | | |
57 | | impl fmt::Display for Address<'_> { |
58 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
59 | 0 | write!(f, "{}", self.full) |
60 | 0 | } |
61 | | } |
62 | | |
63 | | impl PartialEq<&str> for Address<'_> { |
64 | 0 | fn eq(&self, other: &&str) -> bool { |
65 | 0 | self.full == *other |
66 | 0 | } |
67 | | } |
68 | | |
69 | | // A placeholder IP address that can never be constructed |
70 | | #[cfg(not(any(feature = "net", feature = "std")))] |
71 | | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
72 | | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] |
73 | | #[doc(hidden)] |
74 | | pub enum IpAddr {} |
75 | | |
76 | | #[cfg(not(any(feature = "net", feature = "std")))] |
77 | | impl FromStr for IpAddr { |
78 | | type Err = Kind; |
79 | | |
80 | | fn from_str(_: &str) -> Result<Self> { |
81 | | unreachable!() |
82 | | } |
83 | | } |
84 | | |
85 | | /// Information about the host part of an email address |
86 | | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] |
87 | | pub enum Host<'a> { |
88 | | Domain(Name<'a>), |
89 | | IpAddr(IpAddr), |
90 | | } |
91 | | |
92 | | impl<'a> Host<'a> { |
93 | 0 | pub(crate) fn parse<T: List + ?Sized>(list: &T, host: &'a str) -> Result<Host<'a>> { |
94 | 0 | if host.starts_with('[') && host.ends_with(']') { |
95 | 0 | let host_len = host.len(); |
96 | 0 | if host_len < 3 { |
97 | 0 | return Err(Kind::InvalidIpAddr); |
98 | 0 | } |
99 | 0 | if cfg!(not(any(feature = "net", feature = "std"))) { |
100 | 0 | return Err(Kind::NetDisabled); |
101 | 0 | } |
102 | 0 | let ip_addr = host |
103 | 0 | .get(1..host_len - 1) |
104 | 0 | .ok_or(Kind::InvalidIpAddr)? |
105 | 0 | .parse()?; |
106 | 0 | Ok(Host::IpAddr(ip_addr)) |
107 | | } else { |
108 | 0 | Ok(Host::Domain(Name::parse(list, host)?)) |
109 | | } |
110 | 0 | } |
111 | | } |
112 | | |
113 | | #[cfg(test)] |
114 | | mod test { |
115 | | use super::Address; |
116 | | use psl::List; |
117 | | |
118 | | #[test] |
119 | | fn parse() { |
120 | | // Valid email addresses |
121 | | Address::parse(&List, "johndoe@example.com").unwrap(); |
122 | | Address::parse(&List, "john.doe@example.com").unwrap(); |
123 | | Address::parse(&List, "john+doe@example.com").unwrap(); |
124 | | Address::parse(&List, r#""john doe"@example.com"#).unwrap(); |
125 | | |
126 | | // Invalid email addresses |
127 | | Address::parse(&List, "@example.com").unwrap_err(); |
128 | | Address::parse(&List, r#""@example.com"#).unwrap_err(); |
129 | | Address::parse(&List, " @example.com").unwrap_err(); |
130 | | } |
131 | | |
132 | | #[test] |
133 | | fn user() { |
134 | | let email = Address::parse(&List, "johndoe@localhost").unwrap(); |
135 | | assert_eq!(email.user(), "johndoe"); |
136 | | } |
137 | | } |