use std::fmt;
use std::ops::Bound;
use crate::error::DbResult;
use crate::key::Key;
use crate::map_index::Page;
use crate::map_index::{Dir, OrdKey, bound_ordkey, merge_lower, merge_upper};
use super::key_math;
use super::scan::{OrderedScan, OwnedRows};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MappedPage<K, R> {
pub items: Vec<R>,
pub next: Option<K>,
}
#[derive(Clone)]
pub struct Scope<'l, S: OrderedScan + 'l, const N: usize> {
pub(super) scan: S,
pub(super) prefix_key: S::Key,
pub(super) start: Bound<S::Key>,
pub(super) end: Bound<S::Key>,
pub(super) dir: Dir,
pub(super) _lt: std::marker::PhantomData<&'l ()>,
}
struct HexKey<'a, K: Key>(&'a K);
impl<K: Key> fmt::Debug for HexKey<'_, K> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("0x")?;
for byte in Key::as_bytes(self.0) {
write!(f, "{byte:02x}")?;
}
Ok(())
}
}
struct HexBound<'a, K: Key>(&'a Bound<K>);
impl<K: Key> fmt::Debug for HexBound<'_, K> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
Bound::Included(k) => f.debug_tuple("Included").field(&HexKey(k)).finish(),
Bound::Excluded(k) => f.debug_tuple("Excluded").field(&HexKey(k)).finish(),
Bound::Unbounded => f.write_str("Unbounded"),
}
}
}
impl<'l, S: OrderedScan + 'l, const N: usize> fmt::Debug for Scope<'l, S, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Scope")
.field("N", &N)
.field("prefix_key", &HexKey(&self.prefix_key))
.field("start", &HexBound(&self.start))
.field("end", &HexBound(&self.end))
.field("dir", &self.dir)
.finish_non_exhaustive()
}
}
impl<'l, S: OrderedScan + 'l, const N: usize> Scope<'l, S, N> {
pub(crate) fn from_parts(
scan: S,
prefix_key: S::Key,
start: Bound<S::Key>,
end: Bound<S::Key>,
dir: Dir,
) -> Self {
const { key_math::assert_scope_n::<S::Key>(N) };
Self {
scan,
prefix_key,
start,
end,
dir,
_lt: std::marker::PhantomData,
}
}
pub fn range<const M: usize>(mut self, start: Bound<&S::Key>, end: Bound<&S::Key>) -> Self {
const { key_math::assert_range_m::<S::Key>(N, M) };
let (s, e) = key_math::saturate_range_bounds(&self.prefix_key, N, M, start, end);
self.start = s;
self.end = e;
self
}
pub fn asc(mut self) -> Self {
self.dir = Dir::Asc;
self
}
pub fn desc(mut self) -> Self {
self.dir = Dir::Desc;
self
}
pub fn iter(self) -> S::Iter {
self.scan.scan(self.start, self.end, self.dir)
}
pub fn keys(self) -> S::KeyIter {
self.scan.scan_keys(self.start, self.end, self.dir)
}
fn bounds_for_page(&self, after: Option<&S::Key>) -> (Bound<S::Key>, Bound<S::Key>) {
let range_start = bound_ordkey(bound_ref(&self.start));
let range_end = bound_ordkey(bound_ref(&self.end));
match (after, self.dir) {
(None, _) => (clone_bound(&self.start), clone_bound(&self.end)),
(Some(a), Dir::Asc) => {
let lower = merge_lower(range_start, Some(a));
(from_ord(lower), clone_bound(&self.end))
}
(Some(a), Dir::Desc) => {
let upper = merge_upper(range_end, Some(a));
(clone_bound(&self.start), from_ord(upper))
}
}
}
pub fn page_by<R>(
&self,
after: Option<&S::Key>,
limit: usize,
mut f: impl FnMut(S::Row) -> DbResult<Option<R>>,
) -> DbResult<MappedPage<S::Key, R>> {
if limit == 0 {
return Ok(MappedPage {
items: Vec::new(),
next: None,
});
}
let (start, end) = self.bounds_for_page(after);
let mut items = Vec::with_capacity(limit.min(1024));
for row in self.scan.scan(start, end, self.dir) {
let key = S::peek_row_key(&row);
if let Some(r) = f(row)? {
items.push(r);
if items.len() == limit {
return Ok(MappedPage {
items,
next: Some(key),
});
}
}
}
Ok(MappedPage { items, next: None })
}
}
impl<'l, S: OwnedRows + 'l, const N: usize> Scope<'l, S, N> {
pub fn page(&self, after: Option<&S::Key>, limit: usize) -> Page<S::Key, S::Val> {
let mapped = self
.page_by(after, limit, |row| Ok(Some(S::own(row))))
.expect("page closure is infallible");
Page {
items: mapped.items,
next: mapped.next,
}
}
}
fn clone_bound<K: Clone>(b: &Bound<K>) -> Bound<K> {
match b {
Bound::Included(k) => Bound::Included(k.clone()),
Bound::Excluded(k) => Bound::Excluded(k.clone()),
Bound::Unbounded => Bound::Unbounded,
}
}
fn from_ord<K: Key>(b: Bound<OrdKey<K>>) -> Bound<K> {
match b {
Bound::Included(OrdKey(k)) => Bound::Included(k),
Bound::Excluded(OrdKey(k)) => Bound::Excluded(k),
Bound::Unbounded => Bound::Unbounded,
}
}
fn bound_ref<K>(b: &Bound<K>) -> Bound<&K> {
match b {
Bound::Included(k) => Bound::Included(k),
Bound::Excluded(k) => Bound::Excluded(k),
Bound::Unbounded => Bound::Unbounded,
}
}