trine-kv 0.5.13

Embedded LSM MVCC key-value database.
Documentation
use super::{
    BLOB_FILE_EXTENSION, BTreeSet, BlockingStorageObjectListBackend, Error, NativeFileBackend,
    Path, Result, StorageCapability, StorageObjectId, StorageObjectKind, StorageObjectListBackend,
    StorageObjectListRequest, StorageReadBackend, blob_storage_backend,
};

pub(crate) fn list_blob_file_ids(db_path: &Path) -> Result<BTreeSet<u64>> {
    let backend = blob_storage_backend();
    list_blob_file_ids_with_backend(&backend, db_path)
}

pub(crate) fn list_blob_file_ids_with_backend(
    backend: &NativeFileBackend,
    db_path: &Path,
) -> Result<BTreeSet<u64>> {
    backend
        .capabilities()
        .require(StorageCapability::ObjectListing)?;
    let request = StorageObjectListRequest::native_file(StorageObjectKind::Blob, db_path)
        .with_file_extension(BLOB_FILE_EXTENSION);
    blob_file_ids_from_objects(backend.list_objects_blocking(request)?)
}

#[allow(dead_code)]
pub(crate) async fn list_blob_file_ids_with_backend_async<B>(
    backend: &B,
    db_path: &Path,
) -> Result<BTreeSet<u64>>
where
    B: StorageObjectListBackend,
{
    backend
        .capabilities()
        .require(StorageCapability::ObjectListing)?;
    let request = StorageObjectListRequest::native_file(StorageObjectKind::Blob, db_path)
        .with_file_extension(BLOB_FILE_EXTENSION);
    blob_file_ids_from_objects(backend.list_objects(request).await?)
}

pub(super) fn blob_file_ids_from_objects(
    objects: impl IntoIterator<Item = StorageObjectId>,
) -> Result<BTreeSet<u64>> {
    let mut file_ids = BTreeSet::new();
    for object in objects {
        if let Some(file_id) = blob_file_id_from_path(object.path())? {
            file_ids.insert(file_id);
        }
    }

    Ok(file_ids)
}

pub(crate) fn blob_file_id_from_path(path: &Path) -> Result<Option<u64>> {
    let has_blob_extension = path
        .extension()
        .and_then(|extension| extension.to_str())
        .is_some_and(|extension| extension.eq_ignore_ascii_case(BLOB_FILE_EXTENSION));
    if !has_blob_extension {
        return Ok(None);
    }

    let Some(stem) = path.file_stem().and_then(|stem| stem.to_str()) else {
        return Ok(None);
    };
    let Some(file_id) = stem.strip_prefix("blob-") else {
        return Ok(None);
    };
    file_id
        .parse::<u64>()
        .map(Some)
        .map_err(|_| Error::Corruption {
            message: format!("invalid blob file name: {}", path.display()),
        })
}