use std::marker::PhantomData;
use noxu_bind::EntryBinding;
use noxu_db::{Database, DatabaseEntry, Get, OperationStatus, Transaction};
use crate::error::{CollectionError, Result};
pub(crate) fn encode_key<K, KB: EntryBinding<K>>(
binding: &KB,
key: &K,
) -> Result<DatabaseEntry> {
let mut entry = DatabaseEntry::new();
binding
.object_to_entry(key, &mut entry)
.map_err(|e| CollectionError::BindingError(e.to_string()))?;
Ok(entry)
}
pub(crate) fn encode_value<V, VB: EntryBinding<V>>(
binding: &VB,
value: &V,
) -> Result<DatabaseEntry> {
let mut entry = DatabaseEntry::new();
binding
.object_to_entry(value, &mut entry)
.map_err(|e| CollectionError::BindingError(e.to_string()))?;
Ok(entry)
}
pub(crate) fn decode_key<K, KB: EntryBinding<K>>(
binding: &KB,
entry: &DatabaseEntry,
) -> Result<K> {
binding
.entry_to_object(entry)
.map_err(|e| CollectionError::BindingError(e.to_string()))
}
pub(crate) fn decode_value<V, VB: EntryBinding<V>>(
binding: &VB,
entry: &DatabaseEntry,
) -> Result<V> {
binding
.entry_to_object(entry)
.map_err(|e| CollectionError::BindingError(e.to_string()))
}
#[derive(Copy, Clone, Debug)]
pub(crate) enum ScanShape {
KeyValue,
Key,
Value,
}
#[derive(Copy, Clone, Debug)]
pub(crate) enum ScanDirection {
Forward,
Reverse,
}
pub(crate) type StartKey<'a> = Option<&'a [u8]>;
#[allow(clippy::type_complexity)]
pub(crate) fn scan_records<'a, K, V, KB, VB, T, F>(
db: &Database,
txn: Option<&'a Transaction>,
start: StartKey<'a>,
direction: ScanDirection,
key_binding: &KB,
value_binding: &VB,
mut project: F,
) -> Result<Vec<T>>
where
KB: EntryBinding<K>,
VB: EntryBinding<V>,
F: FnMut(K, V) -> T,
{
let mut out: Vec<T> = Vec::new();
let mut cursor = db.open_cursor(txn, None)?;
let mut key = DatabaseEntry::new();
let mut data = DatabaseEntry::new();
let initial_op = match direction {
ScanDirection::Forward => Get::First,
ScanDirection::Reverse => Get::Last,
};
let mut status = cursor.get(&mut key, &mut data, initial_op, None)?;
if !matches!(status, OperationStatus::Success) {
let _ = cursor.close();
return Ok(out);
}
if let Some(bound) = start {
loop {
let cur = key.get_data().unwrap_or(&[]);
let in_range = match direction {
ScanDirection::Forward => cur >= bound,
ScanDirection::Reverse => cur <= bound,
};
if in_range {
break;
}
let step = match direction {
ScanDirection::Forward => Get::Next,
ScanDirection::Reverse => Get::Prev,
};
status = cursor.get(&mut key, &mut data, step, None)?;
if !matches!(status, OperationStatus::Success) {
let _ = cursor.close();
return Ok(out);
}
}
}
loop {
let k = decode_key(key_binding, &key)?;
let v = decode_value(value_binding, &data)?;
out.push(project(k, v));
let step = match direction {
ScanDirection::Forward => Get::Next,
ScanDirection::Reverse => Get::Prev,
};
match cursor.get(&mut key, &mut data, step, None)? {
OperationStatus::Success => continue,
_ => break,
}
}
cursor.close()?;
Ok(out)
}
pub(crate) type Phantom<K, V> = PhantomData<fn() -> (K, V)>;
pub(crate) fn cursor_endpoint<K, V, KB, VB>(
db: &Database,
txn: Option<&Transaction>,
key_binding: &KB,
value_binding: &VB,
which: Get,
) -> Result<Option<(K, V)>>
where
KB: EntryBinding<K>,
VB: EntryBinding<V>,
{
let mut cursor = db.open_cursor(txn, None)?;
let mut key = DatabaseEntry::new();
let mut data = DatabaseEntry::new();
let status = cursor.get(&mut key, &mut data, which, None)?;
let result = match status {
OperationStatus::Success => {
let k = decode_key(key_binding, &key)?;
let v = decode_value(value_binding, &data)?;
Some((k, v))
}
_ => None,
};
cursor.close()?;
Ok(result)
}