reinhardt_auth/core/full_user.rs
1use chrono::{DateTime, Utc};
2
3use crate::core::base_user::BaseUser;
4
5/// FullUser trait - Django's AbstractUser equivalent
6///
7/// This trait extends `BaseUser` with additional user information fields
8/// commonly needed in web applications. It is inspired by Django's AbstractUser.
9///
10/// # Examples
11///
12/// ```no_run
13/// use reinhardt_auth::{BaseUser, FullUser, PasswordHasher};
14/// #[cfg(feature = "argon2-hasher")]
15/// use reinhardt_auth::Argon2Hasher;
16/// use uuid::Uuid;
17/// use chrono::{DateTime, Utc};
18/// use serde::{Serialize, Deserialize};
19///
20/// #[derive(Serialize, Deserialize)]
21/// struct MyUser {
22/// id: Uuid,
23/// username: String,
24/// email: String,
25/// first_name: String,
26/// last_name: String,
27/// password_hash: Option<String>,
28/// last_login: Option<DateTime<Utc>>,
29/// is_active: bool,
30/// is_staff: bool,
31/// is_superuser: bool,
32/// date_joined: DateTime<Utc>,
33/// }
34///
35/// #[cfg(feature = "argon2-hasher")]
36/// impl BaseUser for MyUser {
37/// type PrimaryKey = Uuid;
38/// type Hasher = Argon2Hasher;
39///
40/// fn get_username_field() -> &'static str { "username" }
41/// fn get_username(&self) -> &str { &self.username }
42/// fn password_hash(&self) -> Option<&str> { self.password_hash.as_deref() }
43/// fn set_password_hash(&mut self, hash: String) { self.password_hash = Some(hash); }
44/// fn last_login(&self) -> Option<DateTime<Utc>> { self.last_login }
45/// fn set_last_login(&mut self, time: DateTime<Utc>) { self.last_login = Some(time); }
46/// fn is_active(&self) -> bool { self.is_active }
47/// }
48///
49/// impl FullUser for MyUser {
50/// fn username(&self) -> &str { &self.username }
51/// fn email(&self) -> &str { &self.email }
52/// fn first_name(&self) -> &str { &self.first_name }
53/// fn last_name(&self) -> &str { &self.last_name }
54/// fn is_staff(&self) -> bool { self.is_staff }
55/// fn is_superuser(&self) -> bool { self.is_superuser }
56/// fn date_joined(&self) -> DateTime<Utc> { self.date_joined }
57/// }
58///
59/// # #[cfg(feature = "argon2-hasher")]
60/// # {
61/// let user = MyUser {
62/// id: Uuid::now_v7(),
63/// username: "alice".to_string(),
64/// email: "alice@example.com".to_string(),
65/// first_name: "Alice".to_string(),
66/// last_name: "Smith".to_string(),
67/// password_hash: None,
68/// last_login: None,
69/// is_active: true,
70/// is_staff: true,
71/// is_superuser: false,
72/// date_joined: Utc::now(),
73/// };
74///
75/// assert_eq!(user.get_full_name(), "Alice Smith");
76/// assert_eq!(user.get_short_name(), "Alice");
77/// assert!(user.is_staff());
78/// # }
79/// ```
80pub trait FullUser: BaseUser {
81 /// Returns the username
82 fn username(&self) -> &str;
83
84 /// Returns the email address
85 fn email(&self) -> &str;
86
87 /// Returns the first name
88 fn first_name(&self) -> &str;
89
90 /// Returns the last name
91 fn last_name(&self) -> &str;
92
93 /// Returns whether the user is a staff member
94 ///
95 /// Staff members typically have access to the admin interface.
96 fn is_staff(&self) -> bool;
97
98 /// Returns whether the user is a superuser
99 ///
100 /// Superusers have all permissions without explicit assignment.
101 fn is_superuser(&self) -> bool;
102
103 /// Returns when the user account was created
104 fn date_joined(&self) -> DateTime<Utc>;
105
106 /// Returns the full name (first name + last name)
107 ///
108 /// # Examples
109 ///
110 /// ```no_run
111 /// # use reinhardt_auth::{BaseUser, FullUser, PasswordHasher};
112 /// # #[cfg(feature = "argon2-hasher")]
113 /// # use reinhardt_auth::Argon2Hasher;
114 /// # use uuid::Uuid;
115 /// # use chrono::{DateTime, Utc};
116 /// # use serde::{Serialize, Deserialize};
117 /// # #[derive(Serialize, Deserialize)]
118 /// # struct MyUser { id: Uuid, username: String, email: String,
119 /// # first_name: String, last_name: String, password_hash: Option<String>,
120 /// # last_login: Option<DateTime<Utc>>, is_active: bool, is_staff: bool,
121 /// # is_superuser: bool, date_joined: DateTime<Utc> }
122 /// # #[cfg(feature = "argon2-hasher")]
123 /// # impl BaseUser for MyUser {
124 /// # type PrimaryKey = Uuid;
125 /// # type Hasher = Argon2Hasher;
126 /// # fn get_username_field() -> &'static str { "username" }
127 /// # fn get_username(&self) -> &str { &self.username }
128 /// # fn password_hash(&self) -> Option<&str> { self.password_hash.as_deref() }
129 /// # fn set_password_hash(&mut self, hash: String) { self.password_hash = Some(hash); }
130 /// # fn last_login(&self) -> Option<DateTime<Utc>> { self.last_login }
131 /// # fn set_last_login(&mut self, time: DateTime<Utc>) { self.last_login = Some(time); }
132 /// # fn is_active(&self) -> bool { self.is_active }
133 /// # }
134 /// # impl FullUser for MyUser {
135 /// # fn username(&self) -> &str { &self.username }
136 /// # fn email(&self) -> &str { &self.email }
137 /// # fn first_name(&self) -> &str { &self.first_name }
138 /// # fn last_name(&self) -> &str { &self.last_name }
139 /// # fn is_staff(&self) -> bool { self.is_staff }
140 /// # fn is_superuser(&self) -> bool { self.is_superuser }
141 /// # fn date_joined(&self) -> DateTime<Utc> { self.date_joined }
142 /// # }
143 ///
144 /// # #[cfg(feature = "argon2-hasher")]
145 /// # {
146 /// let user = MyUser {
147 /// id: Uuid::now_v7(),
148 /// username: "bob".to_string(),
149 /// email: "bob@example.com".to_string(),
150 /// first_name: "Bob".to_string(),
151 /// last_name: "Jones".to_string(),
152 /// password_hash: None,
153 /// last_login: None,
154 /// is_active: true,
155 /// is_staff: false,
156 /// is_superuser: false,
157 /// date_joined: Utc::now(),
158 /// };
159 ///
160 /// assert_eq!(user.get_full_name(), "Bob Jones");
161 /// # }
162 /// ```
163 fn get_full_name(&self) -> String {
164 format!("{} {}", self.first_name(), self.last_name())
165 .trim()
166 .to_string()
167 }
168
169 /// Returns the short name (first name only)
170 ///
171 /// # Examples
172 ///
173 /// ```no_run
174 /// # use reinhardt_auth::{BaseUser, FullUser, PasswordHasher};
175 /// # #[cfg(feature = "argon2-hasher")]
176 /// # use reinhardt_auth::Argon2Hasher;
177 /// # use uuid::Uuid;
178 /// # use chrono::{DateTime, Utc};
179 /// # use serde::{Serialize, Deserialize};
180 /// # #[derive(Serialize, Deserialize)]
181 /// # struct MyUser { id: Uuid, username: String, email: String,
182 /// # first_name: String, last_name: String, password_hash: Option<String>,
183 /// # last_login: Option<DateTime<Utc>>, is_active: bool, is_staff: bool,
184 /// # is_superuser: bool, date_joined: DateTime<Utc> }
185 /// # #[cfg(feature = "argon2-hasher")]
186 /// # impl BaseUser for MyUser {
187 /// # type PrimaryKey = Uuid;
188 /// # type Hasher = Argon2Hasher;
189 /// # fn get_username_field() -> &'static str { "username" }
190 /// # fn get_username(&self) -> &str { &self.username }
191 /// # fn password_hash(&self) -> Option<&str> { self.password_hash.as_deref() }
192 /// # fn set_password_hash(&mut self, hash: String) { self.password_hash = Some(hash); }
193 /// # fn last_login(&self) -> Option<DateTime<Utc>> { self.last_login }
194 /// # fn set_last_login(&mut self, time: DateTime<Utc>) { self.last_login = Some(time); }
195 /// # fn is_active(&self) -> bool { self.is_active }
196 /// # }
197 /// # impl FullUser for MyUser {
198 /// # fn username(&self) -> &str { &self.username }
199 /// # fn email(&self) -> &str { &self.email }
200 /// # fn first_name(&self) -> &str { &self.first_name }
201 /// # fn last_name(&self) -> &str { &self.last_name }
202 /// # fn is_staff(&self) -> bool { self.is_staff }
203 /// # fn is_superuser(&self) -> bool { self.is_superuser }
204 /// # fn date_joined(&self) -> DateTime<Utc> { self.date_joined }
205 /// # }
206 ///
207 /// # #[cfg(feature = "argon2-hasher")]
208 /// # {
209 /// let user = MyUser {
210 /// id: Uuid::now_v7(),
211 /// username: "charlie".to_string(),
212 /// email: "charlie@example.com".to_string(),
213 /// first_name: "Charlie".to_string(),
214 /// last_name: "Brown".to_string(),
215 /// password_hash: None,
216 /// last_login: None,
217 /// is_active: true,
218 /// is_staff: false,
219 /// is_superuser: false,
220 /// date_joined: Utc::now(),
221 /// };
222 ///
223 /// assert_eq!(user.get_short_name(), "Charlie");
224 /// # }
225 /// ```
226 fn get_short_name(&self) -> &str {
227 self.first_name()
228 }
229}