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}