1use serde::de::{Error as DeError, Visitor};
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use std::fmt;
4use std::str::FromStr;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct Author {
12 pub name: String,
13 pub email: Option<String>,
14}
15
16impl Author {
17 pub fn new<S: AsRef<str>>(name: S, email: Option<S>) -> Self {
18 Self {
19 name: name.as_ref().to_string(),
20 email: email.map(|s| s.as_ref().to_string()),
21 }
22 }
23}
24
25impl std::fmt::Display for Author {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 if let Some(mail) = &self.email {
28 write!(f, "{} <{}>", self.name, mail)
29 } else {
30 write!(f, "{}", self.name)
31 }
32 }
33}
34
35impl FromStr for Author {
36 type Err = String;
37 fn from_str(s: &str) -> Result<Self, Self::Err> {
38 if let Some(start) = s.find('<') {
39 if let Some(end) = s[start + 1..].find('>') {
40 let name_part = s[..start].trim();
41 let email_part = &s[start + 1..start + 1 + end];
42 if name_part.is_empty() {
43 return Err("Author name is empty".into());
44 }
45 if email_part.is_empty() {
46 return Err("E-mail is empty inside <..>".into());
47 }
48 Ok(Author {
49 name: name_part.to_string(),
50 email: Some(email_part.to_string()),
51 })
52 } else {
53 Err("Missing closing '>' in author mail definition".into())
54 }
55 } else {
56 let name_trim = s.trim();
58 if name_trim.is_empty() {
59 Err("Author string is empty".into())
60 } else {
61 Ok(Author {
62 name: name_trim.to_string(),
63 email: None,
64 })
65 }
66 }
67 }
68}
69
70impl Serialize for Author {
71 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
72 where
73 S: Serializer,
74 {
75 let as_str = self.to_string();
76 serializer.serialize_str(&as_str)
77 }
78}
79
80struct AuthorVisitor;
81
82impl<'de> Visitor<'de> for AuthorVisitor {
83 type Value = Author;
84 fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
85 write!(
86 fmt,
87 "a string of the form \"Name <email@…>\" or just \"Name\""
88 )
89 }
90 fn visit_str<E>(self, v: &str) -> Result<Author, E>
91 where
92 E: DeError,
93 {
94 Author::from_str(v).map_err(DeError::custom)
95 }
96 fn visit_string<E>(self, v: String) -> Result<Author, E>
97 where
98 E: DeError,
99 {
100 Author::from_str(&v).map_err(DeError::custom)
101 }
102}
103
104impl<'de> Deserialize<'de> for Author {
105 fn deserialize<D>(deserializer: D) -> Result<Author, D::Error>
106 where
107 D: Deserializer<'de>,
108 {
109 deserializer.deserialize_string(AuthorVisitor)
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn author_to_string() {
119 let author = Author::new("Klaus", Some("klaus@klausen.de"));
120 assert_eq!(author.to_string(), "Klaus <klaus@klausen.de>");
121 let author = Author::new("Klaus Kinski", None);
122 assert_eq!(author.to_string(), "Klaus Kinski");
123 }
124
125 #[test]
126 fn author_from_string() {
127 let author = "Klaus <klaus@klausen.de>";
128 let a = Author::from_str(&author);
129 assert!(a.is_ok(), "{}", a.unwrap_err());
130 assert_eq!(
131 a.unwrap(),
132 Author {
133 name: "Klaus".to_string(),
134 email: Some("klaus@klausen.de".to_string())
135 }
136 );
137 let author = "Klaus Kinski";
138 let a = Author::from_str(&author);
139 assert!(a.is_ok(), "{}", a.unwrap_err());
140 assert_eq!(
141 a.unwrap(),
142 Author {
143 name: "Klaus Kinski".to_string(),
144 email: None,
145 }
146 );
147 }
148
149 #[test]
150 fn author_deserialize() {
151 let author = r#""Klaus <klaus@klausen.de>""#;
152 let a: Result<Author, _> = serde_json::from_str(author);
153 assert!(a.is_ok(), "{}", a.unwrap_err());
154 assert_eq!(
155 a.unwrap(),
156 Author {
157 name: "Klaus".to_string(),
158 email: Some("klaus@klausen.de".to_string())
159 }
160 );
161 let author = r#""Klaus Kinski""#;
162 let a: Result<Author, _> = serde_json::from_str(author);
163 assert!(a.is_ok(), "{}", a.unwrap_err());
164 assert_eq!(
165 a.unwrap(),
166 Author {
167 name: "Klaus Kinski".to_string(),
168 email: None,
169 }
170 );
171 }
172
173 #[test]
174 fn author_serialize() {
175 let author = Author::new("Klaus", Some("klaus@klausen.de"));
176 let s = serde_json::to_string(&author);
177 assert!(s.is_ok(), "{}", s.unwrap_err());
178 assert_eq!(s.unwrap(), r#""Klaus <klaus@klausen.de>""#);
179
180 let author = Author::new("Klaus Kinski", None);
181 let s = serde_json::to_string(&author);
182 assert!(s.is_ok(), "{}", s.unwrap_err());
183 assert_eq!(s.unwrap(), r#""Klaus Kinski""#);
184 }
185
186 #[test]
187 fn author_unescaped_email() {
188 let author = "Klaus <foo";
189 let a = Author::from_str(&author);
190 assert!(a.is_err());
191 }
192}