//! LevelDB snapshots
//!
//! Snapshots give you a reference to the LevelDB at a certain
//! point in time and won't change while you work with them.
use crate::binding::{leveldb_create_snapshot, leveldb_release_snapshot};
use crate::binding::{leveldb_snapshot_t, leveldb_t};

use crate::database::Database;
use crate::database::kv::KV;
use crate::database::slice::Slice;

use crate::database::error::Error;
use crate::database::iterator::{Iterable, Iterator, Policy};
use crate::database::options::ReadOptions;

#[allow(missing_docs)]
struct RawSnapshot {
    db_ptr: *mut leveldb_t,
    ptr: *mut leveldb_snapshot_t,
}

impl Drop for RawSnapshot {
    fn drop(&mut self) {
        unsafe { leveldb_release_snapshot(self.db_ptr, self.ptr) };
    }
}

/// A LevelDB snapshot
///
/// Represents a LevelDB instance at a certain point in time,
/// and allows for all read operations (get and iteration).
pub struct Snapshot<'a> {
    raw: RawSnapshot,
    database: &'a Database,
}

/// Structs implementing the Snapshots trait can be
/// snapshotted.
pub trait Snapshots {
    /// Creates a snapshot and returns a struct
    /// representing it.
    fn snapshot<'a>(&'a self) -> Snapshot<'a>;
}

impl Snapshots for Database {
    fn snapshot<'a>(&'a self) -> Snapshot<'a> {
        let db_ptr = self.database.ptr;
        let snap = unsafe { leveldb_create_snapshot(db_ptr) };

        let raw = RawSnapshot { db_ptr, ptr: snap };
        Snapshot {
            raw,
            database: self,
        }
    }
}

impl<'a> Snapshot<'a> {
    /// fetches a key from the LevelDB instance
    ///
    /// Inserts this snapshot into ReadOptions before reading
    pub fn get(
        &'a self,
        mut options: ReadOptions<'a>,
        key: Slice,
    ) -> Result<Option<Slice<'static>>, Error> {
        options.snapshot = Some(self);
        self.database.get(options, key)
    }

    #[inline]
    #[allow(missing_docs)]
    pub fn raw_ptr(&self) -> *mut leveldb_snapshot_t {
        self.raw.ptr
    }
}

impl<'a> Iterable<'a> for Snapshot<'a> {
    fn iter_with_policy<T, D, P: Policy>(
        &'a self,
        mut options: ReadOptions<'a>,
        policy: P,
    ) -> Iterator<'a, T, D, P> {
        options.snapshot = Some(self);
        self.database.iter_with_policy(options, policy)
    }
}
