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}