use cid::Cid;
use crate::mst::{Mst, storage::MstStorage};
#[derive(Debug)]
pub struct MstIterator<'a, S: MstStorage> {
mst: &'a Mst<S>,
stack: Vec<StackItem>, }
#[derive(Debug)]
enum StackItem {
VisitNode { cid: Cid, prefix: String },
YieldEntry { key: String, cid: Cid },
}
impl<'a, S: MstStorage> MstIterator<'a, S> {
pub fn new(mst: &'a Mst<S>) -> Self {
let stack = if let Some(root_cid) = mst.root {
vec![StackItem::VisitNode {
cid: root_cid,
prefix: "".to_string(),
}]
} else {
vec![]
};
Self { mst, stack }
}
pub fn seek(&mut self, _target: &str) {
self.stack.clear();
}
}
impl<'a, S: MstStorage> Into<MstIterator<'a, S>> for &'a Mst<S> {
fn into(self) -> MstIterator<'a, S> {
MstIterator::new(self)
}
}
impl<'a, S: MstStorage> MstIterator<'a, S> {
pub fn into_stream(
self,
) -> impl futures::Stream<Item = crate::Result<(String, Cid)>> + 'a + Unpin {
use futures::stream;
Box::pin(stream::unfold(self, |mut iter| async move {
match iter.next_async().await {
Some(result) => Some((result, iter)),
None => None,
}
}))
}
async fn next_async(&mut self) -> Option<crate::Result<(String, Cid)>> {
while let Some(item) = self.stack.pop() {
match item {
StackItem::VisitNode { cid, prefix } => {
let node = match self.mst.get_node(&cid).await {
Ok(Some(node)) => node,
Ok(None) => continue,
Err(e) => return Some(Err(e)),
};
let mut reconstructed_entries = Vec::new();
let mut prev_key_suffix_bytes = Vec::new();
for entry in node.e.iter() {
let suffix_bytes = &entry.k;
let key_suffix_bytes = if entry.p == 0 {
suffix_bytes.to_vec()
} else {
let shared_len = std::cmp::min(entry.p, prev_key_suffix_bytes.len());
let mut key_bytes = Vec::new();
key_bytes.extend_from_slice(&prev_key_suffix_bytes[..shared_len]);
key_bytes.extend_from_slice(suffix_bytes);
key_bytes
};
let key_suffix = String::from_utf8_lossy(&key_suffix_bytes).to_string();
let full_key = format!("{}{}", prefix, key_suffix);
prev_key_suffix_bytes = key_suffix_bytes.clone();
let suffix = key_suffix.clone();
reconstructed_entries.push((full_key, entry, suffix));
}
for (full_key, entry, suffix) in reconstructed_entries.into_iter().rev() {
if let Some(subtree_cid) = &entry.t {
self.stack.push(StackItem::VisitNode {
cid: subtree_cid.clone(),
prefix: full_key.clone(),
});
}
self.stack.push(StackItem::YieldEntry {
key: suffix,
cid: entry.v.clone(),
});
}
if let Some(left) = &node.l {
self.stack.push(StackItem::VisitNode {
cid: left.clone(),
prefix: prefix.clone(),
});
}
}
StackItem::YieldEntry { key, cid } => {
return Some(Ok((key, cid)));
}
}
}
None
}
}