diff --git a/Cargo.lock b/Cargo.lock index 70eadd9..15ba4c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1042,6 +1042,12 @@ dependencies = [ "libc", ] +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + [[package]] name = "fastrand" version = "2.0.0" @@ -3202,6 +3208,7 @@ dependencies = [ "chrono", "diesel", "diesel_migrations", + "fallible-iterator", "filebuffer", "lazy_static", "lexpr", diff --git a/db/Cargo.toml b/db/Cargo.toml index 0852d0c..aff4dcc 100644 --- a/db/Cargo.toml +++ b/db/Cargo.toml @@ -59,6 +59,7 @@ tree_magic_mini = { version = "3.0.2", features = ["with-gpl-data"] } nonempty = "0.6.0" shadow-rs = { version = "0.23", default-features = false } +fallible-iterator = "0.3.0" [build-dependencies] shadow-rs = { version = "0.23", default-features = false } diff --git a/db/src/hierarchies.rs b/db/src/hierarchies.rs index a2a3020..de2f61c 100644 --- a/db/src/hierarchies.rs +++ b/db/src/hierarchies.rs @@ -1,7 +1,10 @@ +use std::collections::HashSet; use std::convert::TryFrom; +use std::iter::{self, FromIterator}; use std::sync::{Arc, Mutex}; use anyhow::{anyhow, Result}; +use fallible_iterator::FallibleIterator; use lru::LruCache; use tracing::trace; use uuid::Uuid; @@ -82,6 +85,57 @@ pub fn list_roots(connection: &UpEndConnection) -> Result> { .collect()) } +pub type AddressPath = (Vec
, Address); + +pub struct HierWalker { + connection: UpEndConnection, + current: Vec, + seen: HashSet
, +} + +impl HierWalker { + pub fn new(connection: UpEndConnection, start: Address) -> Self { + Self { + connection, + current: vec![(vec![], start.clone())], + seen: HashSet::from_iter(iter::once(start)), + } + } +} + +impl FallibleIterator for HierWalker { + type Item = AddressPath; + type Error = anyhow::Error; + + fn next(&mut self) -> Result, Self::Error> { + while let Some((path, address)) = self.current.pop() { + let children: Vec
= self + .connection + .query(Query::SingleQuery(QueryPart::Matches(PatternQuery { + entity: QueryComponent::Variable(None), + attribute: QueryComponent::Exact(ATTR_IN.into()), + value: QueryComponent::Exact(address.clone().into()), + })))? + .into_iter() + .map(|e| e.entity) + .collect(); + + let path: Vec
= path.into_iter().chain(iter::once(address.clone())).collect(); + + for child in children { + if !self.seen.contains(&child) { + self.seen.insert(child.clone()); + self.current.push((path.clone(), child)); + } + } + + return Ok(Some((path, address))); + } + + Ok(None) + } +} + lazy_static! { static ref FETCH_CREATE_LOCK: Mutex<()> = Mutex::new(()); }