saas-rs-sdk 0.6.3

The SaaS RS SDK
use crate::models::model_from_message;
use crate::storage::Error;
use crate::storage::session_store::{FindOptions, SessionStore};
use pbbson::prost::Message;
use serde::{Deserialize, Serialize};
use std::{fmt::Debug, str::FromStr};
use std::{sync::Arc, time::Duration};
use tonic::Status;

#[derive(Clone)]
pub struct SessionDao<B: Clone + Debug + FromStr + ToString> {
    session_store: Arc<dyn SessionStore<B>>,
}

impl<B: Clone + Debug + FromStr + ToString> SessionDao<B> {
    pub fn new(session_store: Arc<dyn SessionStore<B>>) -> Self {
        Self { session_store }
    }

    pub fn adapter(&self) -> Arc<dyn SessionStore<B>> {
        self.session_store.clone()
    }

    pub async fn create<T: Message + Serialize + for<'a> Deserialize<'a> + Clone + Default>(
        &self,
        bucket: B,
        model: &T,
        maybe_expires: Option<Duration>,
    ) -> Result<T, Status> {
        self.session_store
            .create(bucket.clone(), model_from_message(model)?, maybe_expires)
            .await
            .map_err(|e| {
                log::error!(bucket:?, err:? = e; "Failed creating record");
                to_status(e)
            })?
            .try_into()
            .map_err(|e| {
                log::error!(bucket:?, err:? = e; "Failed converting created model back into a message");
                Status::internal(e.to_string())
            })
    }

    pub async fn delete<T: Message + Serialize + for<'a> Deserialize<'a> + Clone + Default>(
        &self,
        bucket: B,
        id: &str,
    ) -> Result<(), Status> {
        self.session_store.delete(bucket.clone(), id).await.map_err(|e| {
            log::error!(bucket:?, id, err:? = e; "Failed deleting record");
            to_status(e)
        })?;
        Ok(())
    }

    pub async fn find<T: Message + Serialize + for<'a> Deserialize<'a> + Clone + Default>(
        &self,
        bucket: B,
        id: &str,
    ) -> Result<T, Status> {
        self.session_store
            .find(bucket.clone(), id)
            .await
            .map_err(|e| {
                log::error!(bucket:?, id, err:? = e; "Failed finding record");
                to_status(e)
            })?
            .try_into()
            .map_err(|e| {
                log::error!(bucket:?, err:? = e; "Failed converting found model back into a message");
                Status::internal(e.to_string())
            })
    }

    pub async fn find_many<T: Message + Serialize + for<'a> Deserialize<'a> + Clone + Default>(
        &self,
        bucket: B,
        options: FindOptions,
    ) -> Result<Vec<T>, Status> {
        let filter = options.filter.clone();
        self.session_store
            .find_many(bucket.clone(), options)
            .await
            .map_err(|e| {
                log::error!(bucket:?, filter:?, err:? = e; "Failed finding records");
                to_status(e)
            })?
            .into_iter()
            .map(|model| {
                model.try_into().map_err(|e| {
                    log::error!(bucket:?, filter:?, err:? = e; "Failed converting found model into a message");
                    to_status(e.into())
                })
            })
            .collect()
    }

    pub async fn update<T: Message + Serialize + for<'a> Deserialize<'a> + Clone + Default>(
        &self,
        bucket: B,
        model: &T,
        maybe_expires: Option<Duration>,
    ) -> Result<T, Status> {
        self.session_store
            .update(bucket.clone(), model_from_message(model)?, maybe_expires)
            .await
            .map_err(|e| {
                log::error!(bucket:?, err:? = e; "Failed updating record");
                to_status(e)
            })?
            .try_into()
            .map_err(|e| {
                log::error!(bucket:?, err:? = e; "Failed converting updated model back into a message");
                Status::internal(e.to_string())
            })
    }
}

fn to_status(e: Error) -> Status {
    match e {
        Error::AlreadyExists(e) => Status::already_exists(e.to_string()),
        Error::FailedPrecondition(e) => Status::failed_precondition(e.to_string()),
        Error::Internal(e) => Status::internal(e.to_string()),
        Error::InvalidArgument(e) => Status::invalid_argument(e.to_string()),
        Error::MigrationFailed(e) => Status::internal(e.to_string()),
        Error::NotFound(e) => Status::not_found(e.to_string()),
        Error::Unavailable(e) => Status::unavailable(e.to_string()),
        Error::Unimplemented(e) => Status::unimplemented(e.to_string()),
    }
}

#[cfg(test)]
mod tests {
    use crate::storage::session_store::SessionDao;
    use crate::storage::session_store::adapters::memory::{MemorySessionStore, Options};
    use std::collections::HashMap;
    use std::sync::Arc;
    use strum_macros::{AsRefStr, Display, EnumIter, EnumString};

    #[test]
    fn can_clone() {
        #[derive(AsRefStr, Clone, Debug, Display, EnumIter, EnumString)]
        enum Bucket {
            None,
        }
        let opts: Options<Bucket> = Options {
            app_name: None,
            belongs_tos_by_bucket: HashMap::new(),
            has_manys_by_bucket: HashMap::new(),
        };
        let session_store = Arc::new(MemorySessionStore::new(opts));
        let session_dao = SessionDao::new(session_store);
        let _ = session_dao.clone();
    }
}