storage-kit 0.0.1

Portable database and transactional state-store Kit.
Documentation
use std::fmt;
#[cfg(feature = "surrealdb")]
use std::future::Future;

use crate::{CapabilitySet, CommonConfig, StorageError, core::ApplyDefaultLocator};

#[derive(Clone, Debug)]
pub(crate) struct DatabaseRuntime {
    common: CommonConfig,
    capabilities: CapabilitySet,
    label: &'static str,
    mode: &'static str,
}

impl DatabaseRuntime {
    pub(crate) fn new(
        common: CommonConfig,
        capabilities: CapabilitySet,
        label: &'static str,
        mode: &'static str,
    ) -> Self {
        Self {
            common,
            capabilities,
            label,
            mode,
        }
    }

    pub(crate) fn capabilities(&self) -> CapabilitySet {
        self.capabilities
    }

    pub(crate) fn backend_mode(&self) -> &'static str {
        self.mode
    }

    #[cfg(feature = "surrealdb")]
    pub(crate) fn common_config(&self) -> &CommonConfig {
        &self.common
    }

    pub(crate) fn normalize_request<T: ApplyDefaultLocator>(&self, request: T) -> T {
        if let Some(default) = self.common.default_locator.as_ref() {
            request.apply_default_locator(default)
        } else {
            request
        }
    }

    pub(crate) fn ensure_mutable(&self, operation: &'static str) -> Result<(), StorageError> {
        self.common.ensure_mutable(operation)
    }

    pub(crate) fn unsupported(
        &self,
        capability: &'static str,
        context: impl Into<String>,
    ) -> StorageError {
        StorageError::unsupported_capability(capability, context.into())
    }

    pub(crate) fn require_capability(
        &self,
        capability: CapabilitySet,
        label: &'static str,
        context: impl Into<String>,
    ) -> Result<(), StorageError> {
        if self.capabilities.contains(capability) {
            Ok(())
        } else {
            Err(self.unsupported(label, context))
        }
    }

    pub(crate) fn map_err(
        &self,
        operation: &'static str,
        error: impl fmt::Display,
    ) -> StorageError {
        StorageError::backend_op(operation, error.to_string())
    }

    #[cfg(feature = "surrealdb")]
    pub(crate) async fn with_request_timeout<T, F>(
        &self,
        operation: &'static str,
        action: impl fmt::Display,
        future: F,
    ) -> Result<T, StorageError>
    where
        F: Future<Output = Result<T, StorageError>> + Send,
        T: Send,
    {
        tokio::time::timeout(self.common.request_timeout(), future)
            .await
            .map_err(|_| {
                StorageError::backend_op(
                    operation,
                    format!(
                        "timed out after {} ms while {action}",
                        self.common.request_timeout_ms
                    ),
                )
            })?
    }

    pub(crate) fn enforce_required_capabilities(&self) -> Result<(), StorageError> {
        if self
            .capabilities
            .contains(self.common.required_capabilities)
        {
            return Ok(());
        }

        Err(StorageError::InvalidConfig(format!(
            "requested capabilities {:?} are not fully satisfied by {} mode `{}` (available: {:?})",
            self.common.required_capabilities, self.label, self.mode, self.capabilities
        )))
    }
}