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