datasources 0.1.5

Async datasource interfaces, middleware, registry, and test doubles
Documentation
use crate::connection::Connection;
use crate::datasource_error::DatasourceError;
use crate::isolation_level::IsolationLevel;
use crate::result::Result;
use crate::stubs::StubConnection;
use crate::transaction::Transaction;
use async_trait::async_trait;
use std::sync::{Arc, Mutex};

pub struct MockTransaction {
    connection: Arc<dyn Connection>,
    isolation: Mutex<IsolationLevel>,
    commit_calls: Mutex<usize>,
    abort_calls: Mutex<usize>,
    commit_error: Mutex<Option<String>>,
    abort_error: Mutex<Option<String>>,
    committed: Mutex<bool>,
    aborted: Mutex<bool>,
}

impl MockTransaction {
    pub fn new_unbound() -> Self {
        Self {
            connection: Arc::new(StubConnection::new("mock")),
            isolation: Mutex::new(IsolationLevel::ReadCommitted),
            commit_calls: Mutex::new(0),
            abort_calls: Mutex::new(0),
            commit_error: Mutex::new(None),
            abort_error: Mutex::new(None),
            committed: Mutex::new(false),
            aborted: Mutex::new(false),
        }
    }

    pub fn set_isolation(&self, isolation: IsolationLevel) {
        *self.isolation.lock().expect("mock") = isolation;
    }

    pub fn set_commit_error(&self, message: impl Into<String>) {
        *self.commit_error.lock().expect("mock") = Some(message.into());
    }

    pub fn set_abort_error(&self, message: impl Into<String>) {
        *self.abort_error.lock().expect("mock") = Some(message.into());
    }

    pub fn commit_calls(&self) -> usize {
        *self.commit_calls.lock().expect("mock")
    }

    pub fn abort_calls(&self) -> usize {
        *self.abort_calls.lock().expect("mock")
    }

    pub fn committed(&self) -> bool {
        *self.committed.lock().expect("mock")
    }

    pub fn aborted(&self) -> bool {
        *self.aborted.lock().expect("mock")
    }
}

#[async_trait]
impl Transaction for MockTransaction {
    fn connection(&self) -> Arc<dyn Connection> {
        Arc::clone(&self.connection)
    }

    fn isolation(&self) -> IsolationLevel {
        *self.isolation.lock().expect("mock")
    }

    async fn commit(&self) -> Result<()> {
        *self.commit_calls.lock().expect("mock") += 1;
        if let Some(error) = &*self.commit_error.lock().expect("mock") {
            return Err(DatasourceError::message(error.clone()));
        }
        *self.committed.lock().expect("mock") = true;
        Ok(())
    }

    async fn abort(&self) -> Result<()> {
        *self.abort_calls.lock().expect("mock") += 1;
        if let Some(error) = &*self.abort_error.lock().expect("mock") {
            return Err(DatasourceError::message(error.clone()));
        }
        *self.aborted.lock().expect("mock") = true;
        Ok(())
    }
}