iridium-db 0.4.0

A high-performance vector-graph hybrid storage and indexing engine
use crate::features::storage::api::{Result, StorageError};

#[cfg(feature = "rhodium-backend")]
use super::{BlobAckMode, BlobDurabilityTarget, BlobPutOptions, BlobPutResult};

#[cfg(feature = "rhodium-backend")]
use rhodium_cache::core::storage::blob::{
    AckPolicy as RhodiumAckPolicy, PutOptions as RhodiumPutOptions, PutResult as RhodiumPutResult,
};
#[cfg(feature = "rhodium-backend")]
use rhodium_cache::core::storage::StorageError as RhodiumStorageError;

pub(super) fn validate_local_blob_id(blob_id: &str) -> Result<()> {
    if blob_id.is_empty() {
        return Err(StorageError::InvalidInput(
            "blob_id must not be empty".to_string(),
        ));
    }
    if blob_id.contains('/') || blob_id.contains('\\') {
        return Err(StorageError::InvalidInput(
            "blob_id must not contain path separators".to_string(),
        ));
    }
    Ok(())
}

#[cfg(feature = "rhodium-backend")]
pub(super) fn validate_rhodium_blob_id(blob_id: &str) -> Result<()> {
    if blob_id.is_empty() {
        return Err(StorageError::InvalidInput(
            "blob_id must not be empty".to_string(),
        ));
    }
    Ok(())
}

pub(super) fn unsupported_prefix_error() -> StorageError {
    StorageError::InvalidInput("prefix listing not supported by this backend".to_string())
}

#[cfg(not(feature = "rhodium-backend"))]
pub(super) fn rhodium_not_wired_message() -> String {
    "rhodium blob backend is not wired yet; enable `rhodium-backend` and complete adapter implementation"
        .to_string()
}

#[cfg(feature = "rhodium-backend")]
pub(super) fn to_rhodium_put_options(options: BlobPutOptions) -> RhodiumPutOptions {
    let overwrite_policy = if options.deny_if_exists {
        rhodium_cache::core::storage::blob::OverwritePolicy::DenyIfExists
    } else {
        rhodium_cache::core::storage::blob::OverwritePolicy::AllowOverwrite
    };
    let target = match options.durability_target {
        BlobDurabilityTarget::Memory => rhodium_cache::core::storage::blob::DurabilityTier::Memory,
        BlobDurabilityTarget::Disk => rhodium_cache::core::storage::blob::DurabilityTier::Disk,
        BlobDurabilityTarget::Remote => rhodium_cache::core::storage::blob::DurabilityTier::Remote,
    };
    let ack = match options.ack_mode {
        BlobAckMode::FireAndForget => RhodiumAckPolicy::FireAndForget,
        BlobAckMode::Flush => RhodiumAckPolicy::Flush {
            target,
            timeout_ms: options.timeout_ms,
        },
    };
    RhodiumPutOptions {
        overwrite_policy,
        ack,
        idempotent: options.idempotent,
        verify_content_hash: options.verify_content_hash,
        quorum_override: None,
        ttl_secs: None,
    }
}

#[cfg(feature = "rhodium-backend")]
pub(super) fn from_rhodium_put_result(result: RhodiumPutResult) -> BlobPutResult {
    BlobPutResult {
        inserted: result.inserted,
        overwritten: result.overwritten,
        idempotent_noop: result.idempotent_noop,
    }
}

#[cfg(feature = "rhodium-backend")]
pub(super) fn map_rhodium_error(err: RhodiumStorageError) -> StorageError {
    match err {
        RhodiumStorageError::NotFound => StorageError::InvalidInput("blob not found".to_string()),
        RhodiumStorageError::AlreadyExists => {
            StorageError::InvalidInput("blob already exists".to_string())
        }
        RhodiumStorageError::Unsupported(message) => StorageError::InvalidInput(message),
        RhodiumStorageError::Retryable { class, message } => {
            StorageError::Sstable(format!("rhodium retryable {:?}: {}", class, message))
        }
        RhodiumStorageError::Terminal { class, message } => {
            StorageError::InvalidInput(format!("rhodium terminal {:?}: {}", class, message))
        }
        RhodiumStorageError::Timeout { timeout_ms } => {
            StorageError::Sstable(format!("rhodium timeout after {}ms", timeout_ms))
        }
        RhodiumStorageError::Internal(message) => {
            StorageError::Sstable(format!("rhodium internal: {}", message))
        }
    }
}

#[cfg(not(feature = "rhodium-backend"))]
#[derive(Debug)]
pub struct RhodiumBlobStore {}

#[cfg(not(feature = "rhodium-backend"))]
impl RhodiumBlobStore {
    pub fn new(root: std::path::PathBuf) -> Result<Self> {
        let _ = root;
        Ok(Self {})
    }
}

#[cfg(not(feature = "rhodium-backend"))]
impl super::BlobStore for RhodiumBlobStore {
    fn put_blob_with_options(
        &mut self,
        _blob_id: &str,
        _bytes: &[u8],
        _options: super::BlobPutOptions,
    ) -> Result<super::BlobPutResult> {
        Err(StorageError::InvalidInput(rhodium_not_wired_message()))
    }

    fn get_blob_with_options(
        &self,
        _blob_id: &str,
        _options: super::BlobReadOptions,
    ) -> Result<Option<super::BlobGetResult>> {
        Err(StorageError::InvalidInput(rhodium_not_wired_message()))
    }

    fn has_blob(&self, _blob_id: &str) -> Result<bool> {
        Err(StorageError::InvalidInput(rhodium_not_wired_message()))
    }

    fn delete_blob(&mut self, _blob_id: &str) -> Result<()> {
        Err(StorageError::InvalidInput(rhodium_not_wired_message()))
    }
}