use serde::{Deserialize, Serialize}; use chrono::{DateTime, Utc}; use bcrypt::{hash, verify, DEFAULT_COST}; /// Represents a user in the system #[derive(Debug, Clone, Serialize, Deserialize)] pub struct User { /// Unique identifier for the user pub id: Option, /// User's full name pub name: String, /// User's email address pub email: String, /// User's hashed password #[serde(skip_serializing)] pub password_hash: Option, /// User's role in the system pub role: UserRole, /// When the user was created pub created_at: Option>, /// When the user was last updated pub updated_at: Option>, } /// Represents the possible roles a user can have #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum UserRole { /// Regular user with limited permissions User, /// Administrator with full permissions Admin, } impl User { /// Creates a new user with default values pub fn new(name: String, email: String) -> Self { Self { id: None, name, email, password_hash: None, role: UserRole::User, created_at: Some(Utc::now()), updated_at: Some(Utc::now()), } } /// Creates a new user with a password pub fn new_with_password(name: String, email: String, password: &str) -> Result { let password_hash = hash(password, DEFAULT_COST)?; Ok(Self { id: None, name, email, password_hash: Some(password_hash), role: UserRole::User, created_at: Some(Utc::now()), updated_at: Some(Utc::now()), }) } /// Creates a new admin user pub fn new_admin(name: String, email: String) -> Self { Self { id: None, name, email, password_hash: None, role: UserRole::Admin, created_at: Some(Utc::now()), updated_at: Some(Utc::now()), } } /// Creates a new admin user with a password pub fn new_admin_with_password(name: String, email: String, password: &str) -> Result { let password_hash = hash(password, DEFAULT_COST)?; Ok(Self { id: None, name, email, password_hash: Some(password_hash), role: UserRole::Admin, created_at: Some(Utc::now()), updated_at: Some(Utc::now()), }) } /// Checks if the user is an admin pub fn is_admin(&self) -> bool { self.role == UserRole::Admin } /// Updates the user's information pub fn update(&mut self, name: Option, email: Option) { if let Some(name) = name { self.name = name; } if let Some(email) = email { self.email = email; } self.updated_at = Some(Utc::now()); } /// Sets or updates the user's password pub fn set_password(&mut self, password: &str) -> Result<(), bcrypt::BcryptError> { let password_hash = hash(password, DEFAULT_COST)?; self.password_hash = Some(password_hash); self.updated_at = Some(Utc::now()); Ok(()) } /// Verifies if the provided password matches the stored hash pub fn verify_password(&self, password: &str) -> Result { match &self.password_hash { Some(hash) => verify(password, hash), None => Ok(false), } } } /// Represents user login credentials #[derive(Debug, Deserialize)] pub struct LoginCredentials { pub email: String, pub password: String, } /// Represents user registration data #[derive(Debug, Deserialize)] pub struct RegistrationData { pub name: String, pub email: String, pub password: String, pub password_confirmation: String, } #[cfg(test)] mod tests { use super::*; #[test] fn test_new_user() { let user = User::new("John Doe".to_string(), "john@example.com".to_string()); assert_eq!(user.name, "John Doe"); assert_eq!(user.email, "john@example.com"); assert!(!user.is_admin()); } #[test] fn test_new_admin() { let admin = User::new_admin("Admin User".to_string(), "admin@example.com".to_string()); assert_eq!(admin.name, "Admin User"); assert_eq!(admin.email, "admin@example.com"); assert!(admin.is_admin()); } #[test] fn test_update_user() { let mut user = User::new("John Doe".to_string(), "john@example.com".to_string()); user.update(Some("Jane Doe".to_string()), None); assert_eq!(user.name, "Jane Doe"); assert_eq!(user.email, "john@example.com"); user.update(None, Some("jane@example.com".to_string())); assert_eq!(user.name, "Jane Doe"); assert_eq!(user.email, "jane@example.com"); } }