git/
user.rs

1/// Represents a user within a commit with a name and email address
2#[derive(Debug, Eq, PartialEq)]
3pub struct User {
4	name: Option<String>,
5	email: Option<String>,
6}
7
8impl User {
9	/// Creates a new user
10	#[inline]
11	#[must_use]
12	pub fn new(name: Option<&str>, email: Option<&str>) -> Self {
13		Self {
14			email: email.map(String::from),
15			name: name.map(String::from),
16		}
17	}
18
19	/// Get the optional name of the user
20	#[inline]
21	#[must_use]
22	pub fn name(&self) -> Option<&str> {
23		self.name.as_deref()
24	}
25
26	/// Get the optional email of the user
27	#[inline]
28	#[must_use]
29	pub fn email(&self) -> Option<&str> {
30		self.email.as_deref()
31	}
32
33	/// Returns `true` if one of name or email is a `Some` value.
34	#[inline]
35	#[must_use]
36	pub const fn is_some(&self) -> bool {
37		self.name.is_some() || self.email.is_some()
38	}
39
40	/// Returns `true` if both name and email is a `None` value.
41	#[inline]
42	#[must_use]
43	pub const fn is_none(&self) -> bool {
44		self.name.is_none() && self.email.is_none()
45	}
46}
47
48impl ToString for User {
49	/// Creates a formatted string of the user
50	///
51	/// The user if formatted with "Name &lt;Email&gt;", which matches the Git CLI. If name or email are
52	/// `None` then they are omitted from the result. If neither are set, and empty is returned.
53	#[inline]
54	fn to_string(&self) -> String {
55		if let Some(name) = self.name.as_ref() {
56			if let Some(email) = self.email.as_ref() {
57				format!("{name} <{email}>")
58			}
59			else {
60				String::from(name)
61			}
62		}
63		else if let Some(email) = self.email.as_ref() {
64			format!("<{email}>")
65		}
66		else {
67			String::new()
68		}
69	}
70}
71
72#[cfg(test)]
73mod tests {
74	use claim::assert_some_eq;
75	use rstest::rstest;
76
77	use super::*;
78
79	#[test]
80	fn name() {
81		let user = User::new(Some("name"), None);
82		assert_some_eq!(user.name(), "name");
83	}
84
85	#[test]
86	fn email() {
87		let user = User::new(None, Some("email"));
88		assert_some_eq!(user.email(), "email");
89	}
90
91	#[rstest]
92	#[case(Some("name"), None)]
93	#[case(None, Some("email"))]
94	#[case(Some("email"), Some("email"))]
95	fn is_some_none_when_some(#[case] name: Option<&str>, #[case] email: Option<&str>) {
96		let user = User::new(name, email);
97		assert!(user.is_some());
98		assert!(!user.is_none());
99	}
100
101	#[test]
102	fn is_some_none_when_none() {
103		let user = User::new(None, None);
104		assert!(!user.is_some());
105		assert!(user.is_none());
106	}
107
108	#[test]
109	fn to_string_with_none_name_and_none_email() {
110		let user = User::new(None, None);
111		assert_eq!(user.to_string(), "");
112	}
113
114	#[test]
115	fn to_string_with_none_name_and_some_email() {
116		let user = User::new(None, Some("me@example.com"));
117		assert_eq!(user.to_string(), "<me@example.com>");
118	}
119
120	#[test]
121	fn to_string_with_some_name_and_none_email() {
122		let user = User::new(Some("Tim Oram"), None);
123		assert_eq!(user.to_string(), "Tim Oram");
124	}
125
126	#[test]
127	fn to_string_with_some_name_and_some_email() {
128		let user = User::new(Some("Tim Oram"), Some("me@example.com"));
129		assert_eq!(user.to_string(), "Tim Oram <me@example.com>");
130	}
131}