opencloudmesh 0.2.1

Implementation of the OpenCloudMesh protocol
Documentation
// SPDX-FileCopyrightText: 2026 Matthias Kraus <info@opengeomesh.org>
//
// SPDX-License-Identifier: LGPL-3.0-or-later

use std::{
    collections::HashMap,
    sync::{Arc, Mutex, PoisonError},
};

// TODO Groups

#[derive(Debug, Clone, Default)]
pub struct User{
    /// local identifier of this user.
    pub id: String,
    /// Display name of this user.
    pub name: String,
}

/// Provide access to users on this OCM Server which can send or recieve shares.
pub trait UserRepo: Clone + Send + Sync {
    fn insert(
        &self,
        user: User,
    ) -> impl std::future::Future<Output = Result<Option<User>, UserRepoError>>
    + std::marker::Send
    + std::marker::Sized;
    fn get(
        &self,
        user_id: &str,
    ) -> impl std::future::Future<Output = Result<User, UserRepoError>>
    + std::marker::Send
    + std::marker::Sized;
    fn remove(
        &self,
        user_id: &str,
    ) -> impl std::future::Future<Output = Result<User, UserRepoError>>
    + std::marker::Send
    + std::marker::Sized;
}

#[derive(Debug, Clone)]
/// Errors when retrieving users.
pub enum UserRepoError {
    RepoAccessFailed,
    NotFound(String),
}

impl<T> From<PoisonError<T>> for UserRepoError {
    fn from(_value: PoisonError<T>) -> Self {
        Self::RepoAccessFailed
    }
}

#[derive(Debug, Clone, Default)]
/// Store Users in memory for testing.
pub struct InMemoryUserRepo {
    map: Arc<Mutex<HashMap<String, User>>>,
}

impl UserRepo for InMemoryUserRepo {
    async fn insert(&self, user: User) -> Result<Option<User>, UserRepoError> {
        Ok(self
            .map
            .lock()
            .unwrap()
            .insert(user.id.clone(), user))
    }

    async fn get(
        &self,
        user_id: &str,
    ) -> Result<User, UserRepoError> {
        self
            .map
            .lock()?
            .get(user_id).cloned()
            .ok_or(UserRepoError::NotFound(user_id.to_string()))
    }

    async fn remove(&self, user_id: &str) -> Result<User, UserRepoError> {
        self
            .map
            .lock()?
            .remove(user_id)
            .ok_or(UserRepoError::NotFound(user_id.to_string()))
    }
}