relay_core/identity/
inbox.rs1use serde::{Deserialize, Serialize};
2
3use crate::prelude::Address;
4
5use super::{IdentityError as Err, canonical_identity_string, is_valid_identity_string};
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub struct InboxId(String);
11
12impl TryFrom<Address> for InboxId {
13 type Error = Err;
14
15 fn try_from(address: Address) -> Result<Self, Err> {
16 match address.inbox {
17 Some(inbox) => Ok(inbox),
18 None => Err(Err::InvalidInbox),
19 }
20 }
21}
22
23impl TryFrom<&str> for InboxId {
24 type Error = Err;
25
26 fn try_from(value: &str) -> Result<Self, Err> {
27 InboxId::parse(value)
28 }
29}
30
31impl TryFrom<String> for InboxId {
32 type Error = Err;
33
34 fn try_from(value: String) -> Result<Self, Err> {
35 InboxId::parse(value)
36 }
37}
38
39impl InboxId {
40 pub fn parse(input: impl AsRef<str>) -> Result<Self, Err> {
49 let input = input.as_ref();
50
51 if input.is_empty() {
52 return Err(Err::InvalidInbox);
53 }
54 if !is_valid_identity_string(input) {
55 return Err(Err::InvalidIdentityString);
56 }
57
58 let canonical = canonical_identity_string(input);
59
60 Ok(Self(canonical))
61 }
62
63 pub fn replace(&mut self, new_value: impl AsRef<str>) -> Result<(), Err> {
65 *self = Self::parse(new_value)?;
66 Ok(())
67 }
68
69 pub fn canonical(&self) -> &str {
72 &self.0
73 }
74}
75
76impl std::fmt::Display for InboxId {
77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 write!(f, "{}", self.canonical())
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn parses_and_canonicalizes() {
88 assert_eq!(InboxId::parse("Work").unwrap().canonical(), "work");
89 }
90
91 #[test]
92 #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: InvalidInbox")]
93 fn rejects_empty() {
94 let _ = InboxId::parse("").unwrap();
95 }
96
97 #[test]
98 #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: InvalidIdentityString")]
99 fn rejects_invalid_chars() {
100 let _ = InboxId::parse("Invalid Inbox!").unwrap();
101 }
102
103 #[test]
104 #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: InvalidIdentityString")]
105 fn reject_untrimmed() {
106 let _ = InboxId::parse(" trim_me ").unwrap();
107 }
108
109 #[test]
110 #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: InvalidIdentityString")]
111 fn rejects_non_ascii() {
112 let _ = InboxId::parse("调试输出").unwrap();
113 }
114
115 #[test]
116 #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: InvalidIdentityString")]
117 fn rejects_homoglyphs() {
118 let _ = InboxId::parse("wоrk").unwrap(); }
120}