iroh_docs/store/fs/
ranges.rs1use redb::{Key, Range, ReadOnlyTable, ReadableTable, Value};
4
5use super::{
6 bounds::{ByKeyBounds, RecordsBounds},
7 into_entry,
8 tables::{RecordsByKeyId, RecordsId, RecordsValue},
9};
10use crate::{store::SortDirection, SignedEntry};
11
12pub trait RangeExt<K: Key, V: Value> {
14 fn next_map<T>(
16 &mut self,
17 map: impl for<'x> Fn(K::SelfType<'x>, V::SelfType<'x>) -> T,
18 ) -> Option<anyhow::Result<T>>;
19
20 fn next_filter_map<T>(
24 &mut self,
25 direction: &SortDirection,
26 filter_map: impl for<'x> Fn(K::SelfType<'x>, V::SelfType<'x>) -> Option<T>,
27 ) -> Option<anyhow::Result<T>>;
28
29 fn next_try_filter_map<T>(
32 &mut self,
33 direction: &SortDirection,
34 filter_map: impl for<'x> Fn(K::SelfType<'x>, V::SelfType<'x>) -> Option<anyhow::Result<T>>,
35 ) -> Option<anyhow::Result<T>> {
36 Some(self.next_filter_map(direction, filter_map)?.and_then(|r| r))
37 }
38}
39
40impl<K: Key + 'static, V: Value + 'static> RangeExt<K, V> for Range<'_, K, V> {
41 fn next_map<T>(
42 &mut self,
43 map: impl for<'x> Fn(K::SelfType<'x>, V::SelfType<'x>) -> T,
44 ) -> Option<anyhow::Result<T>> {
45 self.next()
46 .map(|r| r.map_err(Into::into).map(|r| map(r.0.value(), r.1.value())))
47 }
48
49 fn next_filter_map<T>(
50 &mut self,
51 direction: &SortDirection,
52 filter_map: impl for<'x> Fn(K::SelfType<'x>, V::SelfType<'x>) -> Option<T>,
53 ) -> Option<anyhow::Result<T>> {
54 loop {
55 let next = match direction {
56 SortDirection::Asc => self.next(),
57 SortDirection::Desc => self.next_back(),
58 };
59 match next {
60 None => break None,
61 Some(Err(err)) => break Some(Err(err.into())),
62 Some(Ok(res)) => match filter_map(res.0.value(), res.1.value()) {
63 None => continue,
64 Some(item) => break Some(Ok(item)),
65 },
66 }
67 }
68 }
69}
70
71#[derive(derive_more::Debug)]
73#[debug("RecordsRange")]
74pub struct RecordsRange<'a>(Range<'a, RecordsId<'static>, RecordsValue<'static>>);
75
76impl<'a> RecordsRange<'a> {
79 pub(super) fn with_bounds(
80 records: &'a impl ReadableTable<RecordsId<'static>, RecordsValue<'static>>,
81 bounds: RecordsBounds,
82 ) -> anyhow::Result<Self> {
83 let range = records.range(bounds.as_ref())?;
84 Ok(Self(range))
85 }
86
87 pub(super) fn next_filtered(
92 &mut self,
93 direction: &SortDirection,
94 filter: impl for<'x> Fn(RecordsId<'x>, RecordsValue<'x>) -> bool,
95 ) -> Option<anyhow::Result<SignedEntry>> {
96 self.0
97 .next_filter_map(direction, |k, v| filter(k, v).then(|| into_entry(k, v)))
98 }
99}
100
101impl RecordsRange<'static> {
102 pub(super) fn all_static(
103 records: &ReadOnlyTable<RecordsId<'static>, RecordsValue<'static>>,
104 ) -> anyhow::Result<Self> {
105 let range = records.range::<RecordsId<'static>>(..)?;
106 Ok(Self(range))
107 }
108 pub(super) fn with_bounds_static(
109 records: &ReadOnlyTable<RecordsId<'static>, RecordsValue<'static>>,
110 bounds: RecordsBounds,
111 ) -> anyhow::Result<Self> {
112 let range = records.range(bounds.as_ref())?;
113 Ok(Self(range))
114 }
115}
116
117impl Iterator for RecordsRange<'_> {
118 type Item = anyhow::Result<SignedEntry>;
119 fn next(&mut self) -> Option<Self::Item> {
120 self.0.next_map(into_entry)
121 }
122}
123
124#[derive(derive_more::Debug)]
125#[debug("RecordsByKeyRange")]
126pub struct RecordsByKeyRange {
127 records_table: ReadOnlyTable<RecordsId<'static>, RecordsValue<'static>>,
128 by_key_range: Range<'static, RecordsByKeyId<'static>, ()>,
129}
130
131impl RecordsByKeyRange {
132 pub fn with_bounds(
133 records_by_key_table: ReadOnlyTable<RecordsByKeyId<'static>, ()>,
134 records_table: ReadOnlyTable<RecordsId<'static>, RecordsValue<'static>>,
135 bounds: ByKeyBounds,
136 ) -> anyhow::Result<Self> {
137 let by_key_range = records_by_key_table.range(bounds.as_ref())?;
138 Ok(Self {
139 records_table,
140 by_key_range,
141 })
142 }
143
144 pub fn next_filtered(
148 &mut self,
149 direction: &SortDirection,
150 filter: impl for<'x> Fn(RecordsByKeyId<'x>) -> bool,
151 ) -> Option<anyhow::Result<SignedEntry>> {
152 let entry = self.by_key_range.next_try_filter_map(direction, |k, _v| {
153 if !filter(k) {
154 return None;
155 };
156 let (namespace, key, author) = k;
157 let records_id = (namespace, author, key);
158 let entry = self.records_table.get(&records_id).transpose()?;
159 let entry = entry
160 .map(|value| into_entry(records_id, value.value()))
161 .map_err(anyhow::Error::from);
162 Some(entry)
163 });
164 entry
165 }
166}