contack/name.rs
1use std::fmt;
2use std::fmt::Display;
3
4#[cfg(feature = "read_write")]
5use crate::read_write::component::Component;
6
7/// A name
8///
9/// Stored with all the properties required for the
10/// VCard specification.
11///
12/// Name implements `Display` which allows it to
13/// be formatted in the style of: "Prefix Given
14/// Additional(s) Family Suffix." Additional names
15/// will be separated by spaces.
16///
17/// # Examples
18///
19/// Create a contact with the name "John Doe"
20///
21/// ```rust
22/// use contack::{Name, Contact};
23///
24/// let contact = Contact::new(Name {
25/// given: vec!["John".to_string()],
26/// family: vec!["Doe".to_string()],
27/// ..Default::default()
28/// });
29/// ```
30#[derive(Debug, Clone, PartialEq, PartialOrd, Default)]
31pub struct Name {
32 /// The given name.
33 ///
34 /// Sometimes called a first name.
35 /// The "John" in "John Doe".
36 pub given: Vec<String>,
37 /// Additional names.
38 ///
39 /// Sometimes are called "middle" names. You
40 /// often have more than one.
41 pub additional: Vec<String>,
42 /// The family name.
43 ///
44 /// Sometimes called "last" names. The "Doe" in "John Doe"
45 pub family: Vec<String>,
46 /// Prefixes.
47 ///
48 /// Often times these are things such as "Ms", "Mr" or "Mx",
49 /// although in some cases they can be more advanced, such as
50 /// "Doctor" or "Seargant".
51 pub prefixes: Vec<String>,
52 /// Suffixes
53 ///
54 /// The opposite of prefixes. An example is "Esq".
55 /// Not that common in English.
56 pub suffixes: Vec<String>,
57}
58
59impl Name {
60 /// Creates a new `Name`
61 ///
62 /// For most cases it is probably clearer
63 /// to instantiate the name manually for clarity.
64 ///
65 /// # Example
66 ///
67 /// Create a new name for "John Doe"
68 ///
69 /// ```rust
70 /// use contack::Name;
71 ///
72 /// let name = Name::new(
73 /// Some("John".to_string()),
74 /// Some("Doe".to_string()),
75 /// None,
76 /// None,
77 /// None
78 /// );
79 /// ```
80 #[must_use]
81 pub fn new(
82 given: Option<String>,
83 additional: Option<String>,
84 family: Option<String>,
85 prefixes: Option<String>,
86 suffixes: Option<String>,
87 ) -> Self {
88 Self {
89 given: given.map_or(Vec::new(), |x| vec![x]),
90 additional: additional.map_or(Vec::new(), |x| vec![x]),
91 family: family.map_or(Vec::new(), |x| vec![x]),
92 prefixes: prefixes.map_or(Vec::new(), |x| vec![x]),
93 suffixes: suffixes.map_or(Vec::new(), |x| vec![x]),
94 }
95 }
96
97 /// Converts the name to a single string escaped by spaces.
98 ///
99 /// Each subname is separated by a space, and if
100 /// they contain a space they are prefixed with
101 /// a `\ `. This only exists to be used in the sql
102 /// backend because of backwards compatability.
103 ///
104 /// # Example
105 ///
106 /// Escape someone who has a name with spaces.
107 ///
108 /// ```rust
109 /// use contack::Name;
110 /// let name = vec!["Mary Rose", "Bee", "a\\"];
111 /// assert_eq!(
112 /// Some(String::from(r#"Mary\ Rose Bee a\\"#)),
113 /// Name::escape_with_spaces(name.into_iter())
114 /// );
115 /// ```
116 pub fn escape_with_spaces<'a>(vec: impl Iterator<Item=&'a str>) -> Option<String> {
117 let mut string = String::new();
118 for substring in vec {
119 string.push_str(&substring.replace('\\', "\\\\").replace(' ', r#"\ "#));
120 string.push(' ');
121 }
122 string.pop();
123 if string.is_empty() {
124 None
125 } else {
126 Some(string)
127 }
128 }
129
130 /// Unescapes a name with spaces.
131 ///
132 /// Performs the opposite of [`Name::escape_with_spaces`]
133 ///
134 /// # Example
135 ///
136 /// Unescape someone who has a name with spaces.
137 ///
138 /// ```rust
139 /// use contack::Name;
140 /// let name = r#"Mary\ Rose Bee a\\"#;
141 /// assert_eq!(vec!["Mary Rose", "Bee", "a\\"], Name::unescape_with_spaces(name));
142 /// ```
143 ///
144 /// # Panics
145 ///
146 /// It doesn't, just clippy thinks it does.
147 #[must_use] pub fn unescape_with_spaces(string: &str) -> Vec<String> {
148 let mut in_escape = false;
149 let mut vec = vec![String::new()];
150 for chr in string.chars() {
151 match chr {
152 '\\' if in_escape => vec.last_mut().unwrap().push('\\'),
153 '\\' if !in_escape => in_escape = true,
154 ' ' if in_escape => vec.last_mut().unwrap().push(' '),
155 ' ' if !in_escape => vec.push(String::new()),
156 other => {
157 vec.last_mut().unwrap().push(other);
158 in_escape = false;
159 }
160 }
161 }
162 vec
163 }
164}
165
166impl Display for Name {
167 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168 let mut string = String::new();
169 if !self.prefixes.is_empty() {
170 string.push(' ');
171 string.push_str(&self.prefixes.join(" "))
172 }
173 if !self.given.is_empty() {
174 string.push(' ');
175 string.push_str(&self.given.join(" "))
176 }
177 if !self.additional.is_empty() {
178 string.push(' ');
179 string.push_str(&self.additional.join(" "))
180 }
181 if !self.family.is_empty() {
182 string.push(' ');
183 string.push_str(&self.family.join(" "))
184 }
185 if !self.suffixes.is_empty() {
186 string.push(' ');
187 string.push_str(&self.suffixes.join(" "))
188 }
189
190 write!(f, "{}", string.trim())
191 }
192}
193
194/// Allows a name to be translated into a component.
195///
196/// You should never need to call this directly and should
197/// prefer turning a whole contact into a VCard.
198///
199/// # Example
200///
201/// Convert a name into a contact and print it out.
202///
203///
204/// ```rust
205/// use contack::Name;
206/// use contack::read_write::component::Component;
207///
208/// let name = Name {
209/// given: vec!["John".to_string()],
210/// family: vec!["Doe".to_string()],
211/// ..Default::default()
212/// };
213///
214/// let component: Component = name.into();
215///
216/// // NAME:Doe;John;;;;
217/// println!("{}", component);
218/// ```
219#[cfg(feature = "read_write")]
220#[cfg_attr(feature = "dox", doc(cfg(feature = "read_write")))]
221impl From<Name> for Component {
222 fn from(name: Name) -> Self {
223 Self {
224 name: "N".to_string(),
225 values: vec![
226 name.family,
227 name.given,
228 name.additional,
229 name.prefixes,
230 name.suffixes,
231 ],
232 ..Self::default()
233 }
234 }
235}
236
237#[cfg(feature = "read_write")]
238impl From<Component> for Name {
239 fn from(mut comp: Component) -> Self {
240 comp.values.truncate(5);
241 Self {
242 suffixes: comp.values.pop().unwrap_or_default(),
243 prefixes: comp.values.pop().unwrap_or_default(),
244 additional: comp.values.pop().unwrap_or_default(),
245 given: comp.values.pop().unwrap_or_default(),
246 family: comp.values.pop().unwrap_or_default(),
247 }
248 }
249}