revelation_user/role.rs
1// SPDX-FileCopyrightText: 2025 Revelation Team
2// SPDX-License-Identifier: MIT
3
4//! User role enumeration for authorization.
5//!
6//! This module provides the [`RUserRole`] enum for role-based
7//! access control (RBAC) in the Revelation ecosystem.
8//!
9//! # Role Hierarchy
10//!
11//! Roles have an implicit hierarchy:
12//!
13//! ```text
14//! Admin > Premium > User
15//! ```
16//!
17//! - **Admin**: Full access to all features
18//! - **Premium**: Access to premium features + user features
19//! - **User**: Basic access only
20//!
21//! # Permission Integration
22//!
23//! [`RUserRole`] implements the [`Role`] trait, providing
24//! permission-based access control:
25//!
26//! ```rust
27//! use revelation_user::{Permissions, RUserRole, Role};
28//!
29//! let admin = RUserRole::Admin;
30//! assert!(admin.can(Permissions::DELETE));
31//! assert!(admin.can_all(Permissions::READ | Permissions::WRITE));
32//!
33//! let user = RUserRole::User;
34//! assert!(user.can(Permissions::READ));
35//! assert!(!user.can(Permissions::ADMIN));
36//! ```
37//!
38//! # Database Integration
39//!
40//! With the `db` feature, [`RUserRole`] maps to PostgreSQL enum:
41//!
42//! ```sql
43//! CREATE TYPE user_role AS ENUM ('user', 'premium', 'admin');
44//! ```
45//!
46//! # Examples
47//!
48//! ```rust
49//! use revelation_user::RUserRole;
50//!
51//! let role = RUserRole::Premium;
52//!
53//! // Check capabilities
54//! assert!(role.is_premium()); // Has premium access
55//! assert!(!role.is_admin()); // Not an admin
56//!
57//! // Default is User
58//! assert_eq!(RUserRole::default(), RUserRole::User);
59//! ```
60//!
61//! [`Role`]: crate::Role
62
63use serde::{Deserialize, Serialize};
64
65use crate::{Permissions, Role};
66
67/// User role for authorization decisions.
68///
69/// Defines the access level and capabilities of a user.
70///
71/// # Hierarchy
72///
73/// | Role | Premium Access | Admin Access |
74/// |------|----------------|--------------|
75/// | `User` | No | No |
76/// | `Premium` | Yes | No |
77/// | `Admin` | Yes | Yes |
78///
79/// # Default
80///
81/// The default role is [`RUserRole::User`].
82///
83/// # Examples
84///
85/// ```rust
86/// use revelation_user::RUserRole;
87///
88/// // Default role
89/// let role = RUserRole::default();
90/// assert_eq!(role, RUserRole::User);
91///
92/// // Check access levels
93/// assert!(!RUserRole::User.is_premium());
94/// assert!(RUserRole::Premium.is_premium());
95/// assert!(RUserRole::Admin.is_premium()); // Admins have premium
96/// assert!(RUserRole::Admin.is_admin());
97/// ```
98///
99/// # Serialization
100///
101/// Roles serialize to lowercase snake_case:
102///
103/// ```rust
104/// use revelation_user::RUserRole;
105///
106/// assert_eq!(serde_json::to_string(&RUserRole::User).unwrap(), "\"user\"");
107/// assert_eq!(
108/// serde_json::to_string(&RUserRole::Premium).unwrap(),
109/// "\"premium\""
110/// );
111/// assert_eq!(
112/// serde_json::to_string(&RUserRole::Admin).unwrap(),
113/// "\"admin\""
114/// );
115/// ```
116#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
117#[serde(rename_all = "snake_case")]
118#[cfg_attr(feature = "db", derive(sqlx::Type))]
119#[cfg_attr(
120 feature = "db",
121 sqlx(type_name = "user_role", rename_all = "snake_case")
122)]
123#[cfg_attr(feature = "api", derive(utoipa::ToSchema))]
124pub enum RUserRole {
125 /// Regular user with basic access.
126 ///
127 /// This is the default role for new users.
128 #[default]
129 User,
130
131 /// Premium user with access to premium features.
132 ///
133 /// Includes all User capabilities plus premium content.
134 Premium,
135
136 /// Administrator with full access.
137 ///
138 /// Has all Premium capabilities plus admin functions.
139 Admin
140}
141
142impl RUserRole {
143 /// Check if this role has admin privileges.
144 ///
145 /// Only [`RUserRole::Admin`] returns `true`.
146 ///
147 /// # Examples
148 ///
149 /// ```rust
150 /// use revelation_user::RUserRole;
151 ///
152 /// assert!(RUserRole::Admin.is_admin());
153 /// assert!(!RUserRole::Premium.is_admin());
154 /// assert!(!RUserRole::User.is_admin());
155 /// ```
156 #[must_use]
157 pub const fn is_admin(&self) -> bool {
158 matches!(self, Self::Admin)
159 }
160
161 /// Check if this role has premium access.
162 ///
163 /// Both [`RUserRole::Premium`] and [`RUserRole::Admin`] return `true`.
164 ///
165 /// # Examples
166 ///
167 /// ```rust
168 /// use revelation_user::RUserRole;
169 ///
170 /// assert!(RUserRole::Admin.is_premium());
171 /// assert!(RUserRole::Premium.is_premium());
172 /// assert!(!RUserRole::User.is_premium());
173 /// ```
174 #[must_use]
175 pub const fn is_premium(&self) -> bool {
176 matches!(self, Self::Premium | Self::Admin)
177 }
178
179 /// Check if this is a regular user role.
180 ///
181 /// Only [`RUserRole::User`] returns `true`.
182 ///
183 /// # Examples
184 ///
185 /// ```rust
186 /// use revelation_user::RUserRole;
187 ///
188 /// assert!(RUserRole::User.is_user());
189 /// assert!(!RUserRole::Premium.is_user());
190 /// assert!(!RUserRole::Admin.is_user());
191 /// ```
192 #[must_use]
193 pub const fn is_user(&self) -> bool {
194 matches!(self, Self::User)
195 }
196
197 /// Returns the role as a lowercase string.
198 ///
199 /// # Examples
200 ///
201 /// ```rust
202 /// use revelation_user::RUserRole;
203 ///
204 /// assert_eq!(RUserRole::User.as_str(), "user");
205 /// assert_eq!(RUserRole::Premium.as_str(), "premium");
206 /// assert_eq!(RUserRole::Admin.as_str(), "admin");
207 /// ```
208 #[must_use]
209 pub const fn as_str(&self) -> &'static str {
210 match self {
211 Self::User => "user",
212 Self::Premium => "premium",
213 Self::Admin => "admin"
214 }
215 }
216}
217
218impl core::fmt::Display for RUserRole {
219 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
220 f.write_str(self.as_str())
221 }
222}
223
224/// Implementation of [`Role`] trait for permission-based access control.
225///
226/// # Permission Mapping
227///
228/// | Role | Permissions |
229/// |------|-------------|
230/// | `User` | READ, API_ACCESS |
231/// | `Premium` | READ, WRITE, API_ACCESS, PREMIUM, EXPORT |
232/// | `Admin` | All permissions |
233///
234/// # Examples
235///
236/// ```rust
237/// use revelation_user::{Permissions, RUserRole, Role};
238///
239/// let admin = RUserRole::Admin;
240/// assert!(admin.can(Permissions::MANAGE_USERS));
241/// assert!(admin.can_all(Permissions::all()));
242///
243/// let premium = RUserRole::Premium;
244/// assert!(premium.can(Permissions::PREMIUM));
245/// assert!(premium.can(Permissions::EXPORT));
246/// assert!(!premium.can(Permissions::ADMIN));
247/// ```
248impl Role for RUserRole {
249 fn permissions(&self) -> Permissions {
250 match self {
251 Self::User => Permissions::READ | Permissions::API_ACCESS,
252 Self::Premium => {
253 Permissions::READ
254 | Permissions::WRITE
255 | Permissions::API_ACCESS
256 | Permissions::PREMIUM
257 | Permissions::EXPORT
258 }
259 Self::Admin => Permissions::all()
260 }
261 }
262
263 fn name(&self) -> &'static str {
264 self.as_str()
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271
272 #[test]
273 fn default_is_user() {
274 assert_eq!(RUserRole::default(), RUserRole::User);
275 }
276
277 #[test]
278 fn is_admin_only_for_admin() {
279 assert!(RUserRole::Admin.is_admin());
280 assert!(!RUserRole::Premium.is_admin());
281 assert!(!RUserRole::User.is_admin());
282 }
283
284 #[test]
285 fn is_premium_for_premium_and_admin() {
286 assert!(RUserRole::Admin.is_premium());
287 assert!(RUserRole::Premium.is_premium());
288 assert!(!RUserRole::User.is_premium());
289 }
290
291 #[test]
292 fn serializes_correctly() {
293 assert_eq!(serde_json::to_string(&RUserRole::User).unwrap(), "\"user\"");
294 assert_eq!(
295 serde_json::to_string(&RUserRole::Premium).unwrap(),
296 "\"premium\""
297 );
298 assert_eq!(
299 serde_json::to_string(&RUserRole::Admin).unwrap(),
300 "\"admin\""
301 );
302 }
303
304 #[test]
305 fn deserializes_correctly() {
306 assert_eq!(
307 serde_json::from_str::<RUserRole>("\"user\"").unwrap(),
308 RUserRole::User
309 );
310 assert_eq!(
311 serde_json::from_str::<RUserRole>("\"admin\"").unwrap(),
312 RUserRole::Admin
313 );
314 }
315
316 #[test]
317 fn display_impl() {
318 assert_eq!(format!("{}", RUserRole::User), "user");
319 assert_eq!(format!("{}", RUserRole::Admin), "admin");
320 }
321
322 #[test]
323 fn is_user_only_for_user() {
324 assert!(RUserRole::User.is_user());
325 assert!(!RUserRole::Premium.is_user());
326 assert!(!RUserRole::Admin.is_user());
327 }
328
329 #[test]
330 fn as_str_returns_correct_values() {
331 assert_eq!(RUserRole::User.as_str(), "user");
332 assert_eq!(RUserRole::Premium.as_str(), "premium");
333 assert_eq!(RUserRole::Admin.as_str(), "admin");
334 }
335
336 #[test]
337 fn display_matches_as_str() {
338 assert_eq!(format!("{}", RUserRole::Premium), "premium");
339 }
340
341 #[test]
342 fn role_trait_permissions_user() {
343 let perms = RUserRole::User.permissions();
344 assert!(perms.contains(Permissions::READ));
345 assert!(perms.contains(Permissions::API_ACCESS));
346 assert!(!perms.contains(Permissions::WRITE));
347 assert!(!perms.contains(Permissions::ADMIN));
348 }
349
350 #[test]
351 fn role_trait_permissions_premium() {
352 let perms = RUserRole::Premium.permissions();
353 assert!(perms.contains(Permissions::READ));
354 assert!(perms.contains(Permissions::WRITE));
355 assert!(perms.contains(Permissions::PREMIUM));
356 assert!(perms.contains(Permissions::EXPORT));
357 assert!(!perms.contains(Permissions::ADMIN));
358 }
359
360 #[test]
361 fn role_trait_permissions_admin() {
362 let perms = RUserRole::Admin.permissions();
363 assert_eq!(perms, Permissions::all());
364 }
365
366 #[test]
367 fn role_trait_can() {
368 assert!(RUserRole::Admin.can(Permissions::DELETE));
369 assert!(RUserRole::Premium.can(Permissions::PREMIUM));
370 assert!(!RUserRole::User.can(Permissions::DELETE));
371 }
372
373 #[test]
374 fn role_trait_can_all() {
375 let required = Permissions::READ | Permissions::WRITE;
376 assert!(RUserRole::Admin.can_all(required));
377 assert!(RUserRole::Premium.can_all(required));
378 assert!(!RUserRole::User.can_all(required));
379 }
380
381 #[test]
382 fn role_trait_can_any() {
383 let any_of = Permissions::ADMIN | Permissions::READ;
384 assert!(RUserRole::User.can_any(any_of));
385 assert!(RUserRole::Admin.can_any(any_of));
386 }
387
388 #[test]
389 fn role_trait_name() {
390 assert_eq!(RUserRole::User.name(), "user");
391 assert_eq!(RUserRole::Premium.name(), "premium");
392 assert_eq!(RUserRole::Admin.name(), "admin");
393 }
394}