xapi_rs/data/
email_address.rs1use crate::data::{DataError, Fingerprint};
4use core::fmt;
5use email_address::EmailAddress;
6use serde::{
7 de::{self, Visitor},
8 Deserialize, Deserializer, Serialize, Serializer,
9};
10use std::{
11 cmp::Ordering,
12 hash::{Hash, Hasher},
13 str::FromStr,
14};
15
16#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
20pub struct MyEmailAddress(
21 #[serde(serialize_with = "mbox_ser", deserialize_with = "mbox_des")] EmailAddress,
22);
23
24impl MyEmailAddress {
25 pub(crate) fn from(email: EmailAddress) -> Self {
26 MyEmailAddress(email)
27 }
28
29 pub fn to_uri(&self) -> String {
32 self.0.to_uri()
33 }
34}
35
36impl FromStr for MyEmailAddress {
37 type Err = DataError;
38
39 fn from_str(s: &str) -> Result<Self, Self::Err> {
40 let email = if let Some(x) = s.strip_prefix("mailto:") {
41 x
42 } else {
43 s
44 };
45 Ok(MyEmailAddress::from(EmailAddress::from_str(email)?))
46 }
47}
48
49impl fmt::Display for MyEmailAddress {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 write!(f, "{}", self.0)
52 }
53}
54
55impl Fingerprint for MyEmailAddress {
56 fn fingerprint<H: Hasher>(&self, state: &mut H) {
57 self.0.as_str().to_lowercase().hash(state);
58 }
59}
60
61impl PartialOrd for MyEmailAddress {
62 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
63 self.0
64 .as_str()
65 .to_lowercase()
66 .partial_cmp(&other.0.as_str().to_lowercase())
67 }
68}
69
70impl AsRef<EmailAddress> for MyEmailAddress {
71 fn as_ref(&self) -> &EmailAddress {
72 &self.0
73 }
74}
75
76struct EmailAddressVisitor;
79
80impl Visitor<'_> for EmailAddressVisitor {
81 type Value = EmailAddress;
82
83 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
84 formatter.write_str("an [mailto:]email-address")
85 }
86
87 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
88 where
89 E: de::Error,
90 {
91 let email = if let Some(x) = value.strip_prefix("mailto:") {
92 x
93 } else {
94 value
95 };
96 match EmailAddress::from_str(email) {
97 Ok(x) => Ok(x),
98 Err(x) => Err(de::Error::custom(x)),
99 }
100 }
101}
102
103fn mbox_ser<S>(this: &EmailAddress, ser: S) -> Result<S::Ok, S::Error>
105where
106 S: Serializer,
107{
108 ser.serialize_str(this.to_uri().as_str())
109}
110
111fn mbox_des<'de, D>(des: D) -> Result<EmailAddress, D::Error>
113where
114 D: Deserializer<'de>,
115{
116 des.deserialize_str(EmailAddressVisitor)
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn test_mbox_serde() {
125 #[derive(Debug, Deserialize, Serialize)]
126 struct Foo {
127 mbox: Option<MyEmailAddress>,
128 }
129
130 const IN1: &str = r#"{ }"#;
131 let r1 = serde_json::from_str::<Foo>(&IN1);
132 assert!(r1.is_ok());
133 let v1 = r1.unwrap().mbox;
134 assert!(v1.is_none());
135
136 const IN2: &str = r#"{ "mbox": "example.learner@adlnet.gov" }"#;
137 let r2 = serde_json::from_str::<Foo>(&IN2);
138 assert!(r2.is_ok());
139 let v2 = r2.unwrap().mbox;
140
141 const IN3: &str = r#"{ "mbox": "mailto:example.learner@adlnet.gov" }"#;
142 let r3 = serde_json::from_str::<Foo>(&IN3);
143 assert!(r3.is_ok());
144 let v3 = r3.unwrap().mbox;
145
146 assert_eq!(v2, v3);
147
148 const IN4: &str = r#"{ "mbox": "example.learner_adlnet.gov" }"#;
149 let r4 = serde_json::from_str::<Foo>(&IN4);
150 assert!(r4.is_err());
151
152 const IN5: &str = r#"{ "mbox": "mailto:example.learner_adlnet.gov" }"#;
153 let r5 = serde_json::from_str::<Foo>(&IN5);
154 assert!(r5.is_err());
155 }
156
157 #[test]
158 fn test_email_eq() {
159 let em1 = EmailAddress::from_str("me@gmailbox.net");
160 let em2 = EmailAddress::from_str("me@gmailbox.net");
161 assert_eq!(em1, em2)
162 }
163}