use std::collections::HashMap; use async_trait::async_trait; use tokio::sync::Mutex; use domain::{ entities::User, errors::DomainError, ports::{PasswordHasher, TokenIssuer, UserRepository}, value_objects::{Email, PasswordHash, SystemId}, }; pub struct InMemoryUserRepository { users: Mutex>, } impl InMemoryUserRepository { pub fn new() -> Self { Self { users: Mutex::new(HashMap::new()) } } pub async fn all(&self) -> Vec { self.users.lock().await.values().cloned().collect() } } impl Default for InMemoryUserRepository { fn default() -> Self { Self::new() } } #[async_trait] impl UserRepository for InMemoryUserRepository { async fn find_by_id(&self, id: &SystemId) -> Result, DomainError> { Ok(self.users.lock().await.get(&id.to_string()).cloned()) } async fn find_by_email(&self, email: &Email) -> Result, DomainError> { Ok(self.users.lock().await.values() .find(|u| u.email.as_str() == email.as_str()) .cloned()) } async fn save(&self, user: &User) -> Result<(), DomainError> { self.users.lock().await.insert(user.id.to_string(), user.clone()); Ok(()) } async fn delete(&self, id: &SystemId) -> Result<(), DomainError> { self.users.lock().await.remove(&id.to_string()); Ok(()) } } pub struct StubPasswordHasher; #[async_trait] impl PasswordHasher for StubPasswordHasher { async fn hash(&self, password: &str) -> Result { Ok(PasswordHash::from_hash(format!("hashed:{password}"))) } async fn verify(&self, password: &str, hash: &PasswordHash) -> Result { Ok(hash.as_str() == format!("hashed:{password}")) } } pub struct StubTokenIssuer; #[async_trait] impl TokenIssuer for StubTokenIssuer { async fn issue(&self, user_id: &SystemId, _role: &str) -> Result { Ok(format!("token:{user_id}")) } async fn verify(&self, token: &str) -> Result<(SystemId, String), DomainError> { let id_str = token.strip_prefix("token:").ok_or_else(|| { DomainError::Unauthorized("Invalid stub token".to_string()) })?; let uuid = uuid::Uuid::parse_str(id_str) .map_err(|_| DomainError::Unauthorized("Bad UUID in stub token".to_string()))?; Ok((SystemId::from_uuid(uuid), "user".to_string())) } }