relay_core/identity/
address.rs1use serde::{
2 Deserialize, Deserializer, Serialize, Serializer,
3 de::{self, Visitor},
4};
5
6use super::{AgentId, IdentityError as Err, UserId};
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash)]
9pub struct Address {
10 user: UserId,
11 agent: AgentId,
12}
13
14impl TryFrom<&str> for Address {
15 type Error = Err;
16
17 fn try_from(value: &str) -> Result<Self, Err> {
18 Address::parse(value)
19 }
20}
21
22impl TryFrom<String> for Address {
23 type Error = Err;
24
25 fn try_from(value: String) -> Result<Self, Err> {
26 Address::parse(value)
27 }
28}
29
30impl Address {
31 pub fn new(user: UserId, agent: AgentId) -> Self {
33 Address { user, agent }
34 }
35
36 pub fn parse(input: impl AsRef<str>) -> Result<Self, Err> {
49 let input = input.as_ref();
50 if input.trim() != input {
51 return Err(Err::InvalidAddress);
52 }
53
54 let (user_str, agent_str) = input.rsplit_once('@').ok_or(Err::InvalidAddress)?;
55 let agent = AgentId::parse(agent_str)?;
56 let user = UserId::parse(user_str)?;
57 Ok(Address { user, agent })
58 }
59
60 pub fn canonical(&self) -> String {
63 format!("{}@{}", self.user.canonical(), self.agent.canonical())
64 }
65
66 pub fn user(&self) -> &UserId {
68 &self.user
69 }
70
71 pub fn agent(&self) -> &AgentId {
73 &self.agent
74 }
75}
76
77impl std::fmt::Display for Address {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 write!(f, "{}", self.canonical())
80 }
81}
82
83impl Serialize for Address {
84 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
85 where
86 S: Serializer,
87 {
88 serializer.serialize_str(&self.canonical())
89 }
90}
91
92impl<'de> Deserialize<'de> for Address {
93 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
94 where
95 D: Deserializer<'de>,
96 {
97 struct AddressVisitor;
98
99 impl<'de> Visitor<'de> for AddressVisitor {
100 type Value = Address;
101
102 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
103 f.write_str("a canonical Relay Mail address string")
104 }
105
106 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
107 where
108 E: de::Error,
109 {
110 Address::parse(value)
111 .map_err(|e| de::Error::custom(format!("invalid address `{}`: {}", value, e)))
112 }
113
114 fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
115 where
116 E: de::Error,
117 {
118 self.visit_str(&value)
119 }
120 }
121
122 deserializer.deserialize_str(AddressVisitor)
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn parses_and_canonicalizes() {
132 assert_eq!(
133 Address::parse("#Bob@example.org").unwrap().canonical(),
134 "#bob@example.org"
135 );
136 assert_eq!(
137 Address::parse("Bob@example.org").unwrap().canonical(),
138 "#bob@example.org"
139 );
140 assert_eq!(
141 Address::parse("wORk#Bob@example.ORG").unwrap().canonical(),
142 "work#bob@example.org"
143 );
144 }
145
146 #[test]
147 #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: InvalidAddress")]
148 fn rejects_empty() {
149 let _ = Address::parse("").unwrap();
150 }
151
152 #[test]
153 #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: InvalidAddress")]
154 fn rejects_invalid_chars() {
155 let _ = Address::parse("Invalid Address!").unwrap();
156 }
157
158 #[test]
159 #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: InvalidAddress")]
160 fn reject_untrimmed() {
161 let _ = Address::parse(" trim_me ").unwrap();
162 }
163
164 #[test]
165 #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: InvalidAddress")]
166 fn rejects_non_ascii() {
167 let _ = Address::parse("调试输出").unwrap();
168 }
169
170 #[test]
171 #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: InvalidIdentityString")]
172 fn rejects_homoglyphs() {
173 let _ = Address::parse("wоrk#аlice@us.exaмple.org").unwrap(); }
175}