leveldb-rs-binding 2.2.0

An interface for the LevelDB
Documentation
//! All the option types needed for interfacing with leveldb.
//!
//! Those are:
//! * `Options`: used when opening a database
//! * `ReadOptions`: used when reading from leveldb
//! * `WriteOptions`: used when writng to leveldb
use crate::binding::*;

use crate::database::cache::Cache;
#[cfg(feature = "experimental-extension")]
use crate::database::cache::FlatT3CacheConfig;
use crate::database::comparator::{Comparator, RawComparator};
use crate::database::filter_policy::{
    BloomFilterPolicy, CustomFilterPolicy, FilterPolicy, LevelDBFilterPolicy,
};
use crate::database::snapshots::Snapshot;
use libc::{c_int, size_t};

#[cfg(feature = "experimental-extension")]
use crate::extension::logger::{LogHandler, Logger, OpaqueLogger};

/// Compression choices
#[repr(C)]
#[derive(Copy, Clone)]
pub enum Compression {
    /// No compression enabled
    No = 0,
    /// Enable Snappy compression
    Snappy = 1,
    /// Enable Zstd compression
    ///
    /// Note: LevelDB C FFI does not provide an option to set the Zstd compression level.
    /// It will use the internal default value of 1.
    Zstd = 2,
}

/// Options to consider when opening a new or pre-existing LevelDB.
///
/// Note that in contrast to the LevelDB C API, the Comparator is not
/// passed using this structure.
/// For more detailed explanations, consider the
///
/// [LevelDB documentation](https://github.com/google/leveldb/tree/master/doc)
pub struct Options {
    /// create the database if missing
    ///
    /// default: false
    pub create_if_missing: bool,
    /// report an error if the DB already exists instead of opening.
    ///
    /// default: false
    pub error_if_exists: bool,
    /// paranoid checks make the database report an error as soon as
    /// corruption is detected.
    ///
    /// default: false
    pub paranoid_checks: bool,
    /// Override the size of the write buffer to use.
    ///
    /// default: None
    pub write_buffer_size: Option<size_t>,
    /// Override the max number of open files.
    ///
    /// default: None
    pub max_open_files: Option<i32>,
    /// Override the size of the blocks leveldb uses for writing and caching.
    ///
    /// default: None
    pub block_size: Option<size_t>,
    /// Override the interval between restart points.
    ///
    /// default: None
    pub block_restart_interval: Option<i32>,
    /// Override the maximum size of flushed file before switching to new one.
    ///
    /// default: None
    pub max_file_size: Option<size_t>,
    /// Define whether LevelDB should write compressed or not.
    ///
    /// default: Compression::No
    pub compression: Compression,
    /// A cache to use during read operations.
    ///
    /// default: None
    pub cache: Option<Cache>,
    /// A custom comparator to use for key ordering.
    ///
    /// default: None
    pub comparator: Option<RawComparator>,
    /// A filter policy to use for reducing disk reads.
    ///
    /// default: None
    pub filter_policy: Option<Box<dyn LevelDBFilterPolicy>>,
    /// A custom logger for LevelDB info logging.
    ///
    /// default: None
    #[cfg(feature = "experimental-extension")]
    pub logger: Option<OpaqueLogger>,
}

impl Default for Options {
    fn default() -> Self {
        Self::new()
    }
}

impl Options {
    /// Create a new `Options` struct with default settings.
    pub fn new() -> Options {
        Options {
            create_if_missing: false,
            error_if_exists: false,
            paranoid_checks: false,
            write_buffer_size: None,
            max_open_files: None,
            block_size: None,
            block_restart_interval: None,
            max_file_size: None,
            compression: Compression::No,
            cache: None,
            comparator: None,
            filter_policy: None,
            #[cfg(feature = "experimental-extension")]
            logger: None,
        }
    }

    /// Set a custom comparator for key ordering.
    ///
    /// This method allows users to set a Rust Comparator without dealing with raw C pointers.
    pub fn set_comparator<C: Comparator>(&mut self, comparator: C) {
        self.comparator = Some(RawComparator::new(comparator));
    }

    /// Set a built-in bloom filter policy.
    ///
    /// This is a convenience method for setting the built-in LevelDB bloom filter.
    pub fn set_bloom_filter(&mut self, bits_per_key: i32) {
        let filter_ptr = BloomFilterPolicy::new(bits_per_key);
        self.filter_policy = Some(Box::new(filter_ptr));
    }

    /// Set a custom filter policy.
    ///
    /// This method allows users to set custom filter policies that implement the FilterPolicy trait.
    pub fn set_filter_policy<T: FilterPolicy + 'static>(&mut self, policy: T) {
        let custom_policy = CustomFilterPolicy::new(policy);
        self.filter_policy = Some(Box::new(custom_policy));
    }

    /// Set a custom logger.
    ///
    /// This method allows users to set a custom logger
    /// that will process log messages of LevelDB operations.
    ///
    /// # Type Parameters
    ///
    /// * `H` - The log handler type, must implement `LogHandler` and be `Send + Sync + 'static`
    #[cfg(feature = "experimental-extension")]
    #[deprecated(since = "2.2.0", note = "Use set_log_handler instead")]
    pub fn set_logger<H: LogHandler + Send + Sync + 'static>(&mut self, logger: Logger<H>) {
        let mut opaque_logger = logger.into_opaque();
        opaque_logger.initialize();
        self.logger = Some(opaque_logger);
    }

    /// Set a custom logger.
    ///
    /// This method allows users to set a custom logger
    /// that will process log messages of LevelDB operations.
    ///
    /// # Type Parameters
    ///
    /// * `H` - The log handler type, must implement `LogHandler` and be `Send + Sync + 'static`
    #[cfg(feature = "experimental-extension")]
    pub fn set_log_handler<H: LogHandler + Send + Sync + 'static>(&mut self, handler: H) {
        let logger = Logger::new(handler);
        let mut opaque_logger = logger.into_opaque();
        opaque_logger.initialize();
        self.logger = Some(opaque_logger);
    }

    /// Set a built-in LRU cache with specified capacity
    #[allow(deprecated)]
    pub fn set_builtin_lru_cache(&mut self, capacity: size_t) {
        self.cache = Some(Cache::new(capacity));
    }

    /// Set a FlatT3Cache with specified config
    #[cfg(feature = "experimental-extension")]
    #[allow(deprecated)]
    pub fn set_flat_t3_cache(&mut self, config: FlatT3CacheConfig) {
        self.cache = Some(Cache::new_flat_t3_cache(config));
    }
}

/// The write options to use for a write operation.
#[derive(Copy, Clone)]
pub struct WriteOptions {
    /// `fsync` before acknowledging a write operation.
    ///
    /// default: false
    pub sync: bool,
}

impl Default for WriteOptions {
    fn default() -> Self {
        Self::new()
    }
}

impl WriteOptions {
    /// Return a new `WriteOptions` struct with default settings.
    pub fn new() -> WriteOptions {
        WriteOptions { sync: false }
    }
}

/// The read options to use for any read operation.
#[allow(missing_copy_implementations)]
pub struct ReadOptions<'a> {
    /// Whether to verify the saved checksums on read.
    ///
    /// default: false
    pub verify_checksums: bool,
    /// Whether to fill the internal cache with the
    /// results of the read.
    ///
    /// default: true
    pub fill_cache: bool,
    /// An optional snapshot to base this operation on.
    ///
    /// Consider using the `Snapshot` trait instead of setting
    /// this manually.
    ///
    /// default: None
    pub snapshot: Option<&'a Snapshot<'a>>,
}

impl<'a> Default for ReadOptions<'a> {
    fn default() -> Self {
        Self::new()
    }
}

impl<'a> ReadOptions<'a> {
    /// Return a `ReadOptions` struct with the default values.
    pub fn new() -> ReadOptions<'a> {
        ReadOptions {
            verify_checksums: false,
            fill_cache: true,
            snapshot: None,
        }
    }
}

/// Convert Rust Options to LevelDB C options.
///
/// # Safety
///
/// This function is unsafe because it creates and manipulates raw LevelDB C objects.
/// The caller must ensure that:
/// - The returned pointer is properly managed and eventually passed to leveldb_options_destroy
/// - The Options reference remains valid for the duration of this call
pub(crate) unsafe fn c_options(options: &Options) -> *mut leveldb_options_t {
    unsafe {
        let c_options = leveldb_options_create();
        leveldb_options_set_create_if_missing(c_options, options.create_if_missing as u8);
        leveldb_options_set_error_if_exists(c_options, options.error_if_exists as u8);
        leveldb_options_set_paranoid_checks(c_options, options.paranoid_checks as u8);
        if let Some(wbs) = options.write_buffer_size {
            leveldb_options_set_write_buffer_size(c_options, wbs);
        }
        if let Some(mf) = options.max_open_files {
            leveldb_options_set_max_open_files(c_options, mf);
        }
        if let Some(bs) = options.block_size {
            leveldb_options_set_block_size(c_options, bs);
        }
        if let Some(bi) = options.block_restart_interval {
            leveldb_options_set_block_restart_interval(c_options, bi);
        }
        if let Some(mfs) = options.max_file_size {
            leveldb_options_set_max_file_size(c_options, mfs);
        }
        leveldb_options_set_compression(c_options, options.compression as c_int);
        if let Some(ref c) = options.comparator {
            leveldb_options_set_comparator(c_options, c.raw_ptr());
        }
        if let Some(ref cache) = options.cache {
            leveldb_options_set_cache(c_options, cache.raw_ptr());
        }
        if let Some(ref fp) = options.filter_policy {
            leveldb_options_set_filter_policy(c_options, fp.raw_ptr());
        }

        #[cfg(feature = "experimental-extension")]
        if let Some(ref logger) = options.logger {
            leveldb_options_set_info_log(c_options, logger.raw_ptr());
        }

        c_options
    }
}

/// Convert Rust WriteOptions to LevelDB C write options.
///
/// # Safety
///
/// This function is unsafe because it creates and manipulates raw LevelDB C objects.
/// The caller must ensure that:
/// - The returned pointer is properly managed and eventually passed to leveldb_writeoptions_destroy
pub(crate) unsafe fn c_writeoptions(options: WriteOptions) -> *mut leveldb_writeoptions_t {
    unsafe {
        let c_writeoptions = leveldb_writeoptions_create();
        leveldb_writeoptions_set_sync(c_writeoptions, options.sync as u8);
        c_writeoptions
    }
}

/// Convert Rust ReadOptions to LevelDB C read options.
///
/// # Safety
///
/// This function is unsafe because it creates and manipulates raw LevelDB C objects.
/// The caller must ensure that:
/// - The returned pointer is properly managed and eventually passed to leveldb_readoptions_destroy
/// - The ReadOptions reference and any contained snapshot remain valid for the duration of this call
pub(crate) unsafe fn c_readoptions<'a>(options: &ReadOptions<'a>) -> *mut leveldb_readoptions_t {
    unsafe {
        let c_readoptions = leveldb_readoptions_create();
        leveldb_readoptions_set_verify_checksums(c_readoptions, options.verify_checksums as u8);
        leveldb_readoptions_set_fill_cache(c_readoptions, options.fill_cache as u8);

        if let Some(snapshot) = options.snapshot {
            leveldb_readoptions_set_snapshot(c_readoptions, snapshot.raw_ptr());
        }
        c_readoptions
    }
}