planter_core/person.rs
1use anyhow::Context;
2pub use email_address::EmailAddress;
3use nutype::nutype;
4pub use phonenumber::PhoneNumber;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
7/// Represents a person with a name and contact information.
8pub struct Person {
9 /// The first name of the person.
10 first_name: NameString,
11 /// The last name of the person.
12 last_name: NameString,
13 /// The email address of the person.
14 email: Option<EmailAddress>,
15 /// The phone number of the person.
16 phone: Option<PhoneNumber>,
17}
18
19#[nutype(
20 sanitize(trim),
21 validate(not_empty, len_char_max = NAME_LEN),
22 derive(Debug, Eq, PartialEq, Clone, Display, Deref)
23)]
24pub struct NameString(String);
25
26impl Person {
27 /// Create a new `Person` with the given name.
28 ///
29 /// # Arguments
30 /// * `name` - The name of the person.
31 ///
32 /// # Returns
33 /// A new `Person` instance.
34 ///
35 /// # Examples
36 /// ```
37 /// use planter_core::person::Person;
38 ///
39 /// let person = Person::new("Margherita", "Hack").unwrap();
40 /// ```
41 pub fn new(name: impl Into<String>, surname: impl Into<String>) -> Option<Self> {
42 let (name, surname) = match (NameString::try_new(name), NameString::try_new(surname)) {
43 (Ok(n), Ok(s)) => (n, s),
44 _ => return None,
45 };
46
47 Some(Person {
48 first_name: name,
49 last_name: surname,
50 email: None,
51 phone: None,
52 })
53 }
54
55 /// Add or edit the email address of the person.
56 ///
57 /// # Arguments
58 /// * `email` - The new email address of the person.
59 ///
60 /// # Examples
61 /// ```
62 /// use planter_core::person::Person;
63 /// use email_address::EmailAddress;
64 /// use std::str::FromStr;
65 ///
66 /// let mut person = Person::new("Margherita", "Hack").unwrap();
67 /// let email = EmailAddress::from_str("margherita.hack@example.com").unwrap();
68 /// person.update_email(email.clone());
69 /// assert_eq!(person.email(), &Some(email));
70 /// ```
71 pub fn update_email(&mut self, email: EmailAddress) {
72 self.email = Some(email);
73 }
74
75 /// Remove the email address of the person.
76 ///
77 /// # Examples
78 /// ```
79 /// use planter_core::person::Person;
80 /// use email_address::EmailAddress;
81 /// use std::str::FromStr;
82 ///
83 /// let mut person = Person::new("Margherita", "Hack").unwrap();
84 /// let email = EmailAddress::from_str("margherita.hack@example.com").unwrap();
85 /// person.update_email(email.clone());
86 /// assert_eq!(person.email(), &Some(email));
87 /// person.rm_email();
88 /// assert!(person.email().is_none());
89 /// ```
90 pub fn rm_email(&mut self) {
91 self.email = None;
92 }
93
94 /// Add or edit the phone number of the person.
95 ///
96 /// # Arguments
97 /// * `phone` - The new phone number of the person.
98 ///
99 /// # Examples
100 /// ```
101 /// use planter_core::person::Person;
102 /// use std::str::FromStr;
103 /// use phonenumber::PhoneNumber;
104 ///
105 /// let mut person = Person::new("Margherita", "Hack").unwrap();
106 /// let phone = PhoneNumber::from_str("+1234567890").unwrap();
107 /// person.update_phone(phone.clone());
108 /// assert_eq!(person.phone(), &Some(phone));
109 /// ```
110 pub fn update_phone(&mut self, phone: PhoneNumber) {
111 self.phone = Some(phone);
112 }
113
114 /// Remove the phone number of the person.
115 ///
116 /// # Examples
117 /// ```
118 /// use planter_core::person::Person;
119 /// use std::str::FromStr;
120 /// use phonenumber::PhoneNumber;
121 ///
122 /// let mut person = Person::new("Margherita", "Hack").unwrap();
123 /// let phone = PhoneNumber::from_str("+1234567890").unwrap();
124 /// person.update_phone(phone.clone());
125 /// assert_eq!(person.phone(), &Some(phone));
126 /// person.rm_phone();
127 /// assert!(person.phone().is_none());
128 /// ```
129 pub fn rm_phone(&mut self) {
130 self.phone = None;
131 }
132
133 /// Get the phone number of the person.
134 ///
135 /// # Examples
136 /// ```
137 /// use planter_core::person::Person;
138 /// use phonenumber::PhoneNumber;
139 /// use std::str::FromStr;
140 ///
141 /// let mut person = Person::new("Margherita", "Hack").unwrap();
142 /// let phone = PhoneNumber::from_str("+1234567890").unwrap();
143 /// person.update_phone(phone.clone());
144 /// assert_eq!(person.phone(), &Some(phone));
145 /// ```
146 pub fn phone(&self) -> &Option<PhoneNumber> {
147 &self.phone
148 }
149
150 /// Get the email of the person.
151 ///
152 /// # Examples
153 /// ```
154 /// use planter_core::person::Person;
155 /// use email_address::EmailAddress;
156 /// use std::str::FromStr;
157 ///
158 /// let mut person = Person::new("Margherita", "Hack").unwrap();
159 /// let email = EmailAddress::from_str("margherita.hack@example.com").unwrap();
160 /// person.update_email(email.clone());
161 /// assert_eq!(person.email(), &Some(email));
162 /// ```
163 pub fn email(&self) -> &Option<EmailAddress> {
164 &self.email
165 }
166
167 /// Get the name of the person.
168 ///
169 /// # Examples
170 /// ```
171 /// use planter_core::person::Person;
172 ///
173 /// let mut person = Person::new("Margherita", "Hack").unwrap();
174 /// assert_eq!(person.full_name(), "Margherita Hack");
175 /// ```
176 pub fn full_name(&self) -> String {
177 format!("{} {}", self.first_name, self.last_name)
178 }
179
180 /// Get the first name of the person.
181 ///
182 /// # Examples
183 /// ```
184 /// use planter_core::person::Person;
185 ///
186 /// let person = Person::new("Margherita", "Hack").unwrap();
187 /// assert_eq!(person.first_name(), "Margherita");
188 /// ```
189 pub fn first_name(&self) -> &str {
190 &self.first_name
191 }
192
193 /// Update the first name of the person.
194 ///
195 /// # Errors
196 ///
197 /// It can return an error, if the input `name` can't be converted to
198 /// `NameString`
199 ///
200 /// # Examples
201 ///
202 /// ```
203 /// use planter_core::person::Person;
204 ///
205 /// let mut person = Person::new("Margaret", "Hack").unwrap();
206 /// person.update_first_name("Margherita").unwrap();
207 /// assert_eq!(person.first_name(), "Margherita");
208 /// ```
209 pub fn update_first_name(&mut self, name: impl Into<String>) -> anyhow::Result<()> {
210 self.first_name =
211 NameString::try_new(name).context("Input can't be converted into NameString.")?;
212 Ok(())
213 }
214
215 /// Get the last name of the person.
216 ///
217 /// # Examples
218 /// ```
219 /// use planter_core::person::Person;
220 ///
221 /// let mut person = Person::new("Margherita", "Hack").unwrap();
222 /// assert_eq!(person.last_name(), "Hack");
223 /// ```
224 pub fn last_name(&self) -> &str {
225 &self.last_name
226 }
227
228 /// Update the last name of the person.
229 ///
230 /// # Errors
231 ///
232 /// It can return an error, if the input `name` can't be converted to
233 /// `NameString`
234 ///
235 /// # Examples
236 ///
237 /// ```
238 /// use planter_core::person::Person;
239 ///
240 /// let mut person = Person::new("Margherita", "Hacker").unwrap();
241 /// person.update_last_name("Hack").unwrap();
242 /// assert_eq!(person.last_name(), "Hack");
243 /// ```
244 pub fn update_last_name(&mut self, name: impl Into<String>) -> anyhow::Result<()> {
245 self.last_name =
246 NameString::try_new(name).context("Input can't be converted into NameString.")?;
247 Ok(())
248 }
249}
250
251const NAME_LEN: usize = 50;
252
253#[cfg(test)]
254/// Test utilities for the `person` module.
255pub mod test_utils {
256 use std::str::FromStr;
257
258 use email_address::EmailAddress;
259 use phonenumber::PhoneNumber;
260 use proptest::prelude::Strategy;
261
262 /// Generate a random email address.
263 pub fn email() -> impl Strategy<Value = EmailAddress> {
264 r"^\+?[1-9][0-9]{7,14}$".prop_map(|s: String| EmailAddress::from_str(&s).unwrap())
265 }
266
267 /// Generate a random phone number.
268 pub fn phone_number() -> impl Strategy<Value = PhoneNumber> {
269 r"^\d{3}-\d{3}-\d{4}$".prop_map(|s: String| PhoneNumber::from_str(&s).unwrap())
270 }
271}