80 lines
2.4 KiB
Rust
80 lines
2.4 KiB
Rust
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<HashMap<String, User>>,
|
|
}
|
|
|
|
impl InMemoryUserRepository {
|
|
pub fn new() -> Self {
|
|
Self { users: Mutex::new(HashMap::new()) }
|
|
}
|
|
|
|
pub async fn all(&self) -> Vec<User> {
|
|
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<Option<User>, DomainError> {
|
|
Ok(self.users.lock().await.get(&id.to_string()).cloned())
|
|
}
|
|
|
|
async fn find_by_email(&self, email: &Email) -> Result<Option<User>, 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<PasswordHash, DomainError> {
|
|
Ok(PasswordHash::from_hash(format!("hashed:{password}")))
|
|
}
|
|
async fn verify(&self, password: &str, hash: &PasswordHash) -> Result<bool, DomainError> {
|
|
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<String, DomainError> {
|
|
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()))
|
|
}
|
|
}
|