use redb::{Key, Range, ReadOnlyTable, ReadableTable, Value};
use super::{
bounds::{ByKeyBounds, RecordsBounds},
into_entry,
tables::{RecordsByKeyId, RecordsId, RecordsValue},
};
use crate::{store::SortDirection, SignedEntry};
pub trait RangeExt<K: Key, V: Value> {
fn next_map<T>(
&mut self,
map: impl for<'x> Fn(K::SelfType<'x>, V::SelfType<'x>) -> T,
) -> Option<anyhow::Result<T>>;
fn next_filter_map<T>(
&mut self,
direction: &SortDirection,
filter_map: impl for<'x> Fn(K::SelfType<'x>, V::SelfType<'x>) -> Option<T>,
) -> Option<anyhow::Result<T>>;
fn next_try_filter_map<T>(
&mut self,
direction: &SortDirection,
filter_map: impl for<'x> Fn(K::SelfType<'x>, V::SelfType<'x>) -> Option<anyhow::Result<T>>,
) -> Option<anyhow::Result<T>> {
Some(self.next_filter_map(direction, filter_map)?.and_then(|r| r))
}
}
impl<K: Key + 'static, V: Value + 'static> RangeExt<K, V> for Range<'_, K, V> {
fn next_map<T>(
&mut self,
map: impl for<'x> Fn(K::SelfType<'x>, V::SelfType<'x>) -> T,
) -> Option<anyhow::Result<T>> {
self.next()
.map(|r| r.map_err(Into::into).map(|r| map(r.0.value(), r.1.value())))
}
fn next_filter_map<T>(
&mut self,
direction: &SortDirection,
filter_map: impl for<'x> Fn(K::SelfType<'x>, V::SelfType<'x>) -> Option<T>,
) -> Option<anyhow::Result<T>> {
loop {
let next = match direction {
SortDirection::Asc => self.next(),
SortDirection::Desc => self.next_back(),
};
match next {
None => break None,
Some(Err(err)) => break Some(Err(err.into())),
Some(Ok(res)) => match filter_map(res.0.value(), res.1.value()) {
None => continue,
Some(item) => break Some(Ok(item)),
},
}
}
}
}
#[derive(derive_more::Debug)]
#[debug("RecordsRange")]
pub struct RecordsRange<'a>(Range<'a, RecordsId<'static>, RecordsValue<'static>>);
impl<'a> RecordsRange<'a> {
pub(super) fn with_bounds(
records: &'a impl ReadableTable<RecordsId<'static>, RecordsValue<'static>>,
bounds: RecordsBounds,
) -> anyhow::Result<Self> {
let range = records.range(bounds.as_ref())?;
Ok(Self(range))
}
pub(super) fn next_filtered(
&mut self,
direction: &SortDirection,
filter: impl for<'x> Fn(RecordsId<'x>, RecordsValue<'x>) -> bool,
) -> Option<anyhow::Result<SignedEntry>> {
self.0
.next_filter_map(direction, |k, v| filter(k, v).then(|| into_entry(k, v)))
}
}
impl RecordsRange<'static> {
pub(super) fn all_static(
records: &ReadOnlyTable<RecordsId<'static>, RecordsValue<'static>>,
) -> anyhow::Result<Self> {
let range = records.range::<RecordsId<'static>>(..)?;
Ok(Self(range))
}
pub(super) fn with_bounds_static(
records: &ReadOnlyTable<RecordsId<'static>, RecordsValue<'static>>,
bounds: RecordsBounds,
) -> anyhow::Result<Self> {
let range = records.range(bounds.as_ref())?;
Ok(Self(range))
}
}
impl Iterator for RecordsRange<'_> {
type Item = anyhow::Result<SignedEntry>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next_map(into_entry)
}
}
#[derive(derive_more::Debug)]
#[debug("RecordsByKeyRange")]
pub struct RecordsByKeyRange {
records_table: ReadOnlyTable<RecordsId<'static>, RecordsValue<'static>>,
by_key_range: Range<'static, RecordsByKeyId<'static>, ()>,
}
impl RecordsByKeyRange {
pub fn with_bounds(
records_by_key_table: ReadOnlyTable<RecordsByKeyId<'static>, ()>,
records_table: ReadOnlyTable<RecordsId<'static>, RecordsValue<'static>>,
bounds: ByKeyBounds,
) -> anyhow::Result<Self> {
let by_key_range = records_by_key_table.range(bounds.as_ref())?;
Ok(Self {
records_table,
by_key_range,
})
}
pub fn next_filtered(
&mut self,
direction: &SortDirection,
filter: impl for<'x> Fn(RecordsByKeyId<'x>) -> bool,
) -> Option<anyhow::Result<SignedEntry>> {
let entry = self.by_key_range.next_try_filter_map(direction, |k, _v| {
if !filter(k) {
return None;
};
let (namespace, key, author) = k;
let records_id = (namespace, author, key);
let entry = self.records_table.get(&records_id).transpose()?;
let entry = entry
.map(|value| into_entry(records_id, value.value()))
.map_err(anyhow::Error::from);
Some(entry)
});
entry
}
}