use std::collections::HashSet;
use super::compat::{EntityRef, Link, LinkStore, StorageContext};
use crate::error::Result;
use super::helpers::{get_edge_weight, get_neighbor_entity, get_neighbors, matches_link_type};
use super::types::{PathNode, TraversalOptions, MAX_TRAVERSAL_DEPTH, MAX_TRAVERSAL_RESULTS};
pub async fn dfs_traverse<S: LinkStore>(
store: &S,
ctx: &StorageContext,
start: EntityRef,
options: &TraversalOptions,
) -> Result<Vec<PathNode>> {
let max_depth = options.max_depth.min(MAX_TRAVERSAL_DEPTH);
let limit = options
.limit
.unwrap_or(MAX_TRAVERSAL_RESULTS)
.min(MAX_TRAVERSAL_RESULTS);
let min_weight = options.min_weight.unwrap_or(f64::NEG_INFINITY);
let mut visited: HashSet<EntityRef> = HashSet::new();
let mut results: Vec<PathNode> = Vec::new();
let mut stack: Vec<(EntityRef, usize, f64, Option<Link>)> = Vec::new();
stack.push((start, 0, 0.0, None));
while let Some((current, depth, path_weight, via_link)) = stack.pop() {
if visited.contains(¤t) {
continue;
}
visited.insert(current.clone());
results.push(PathNode {
entity_id: current.clone(),
depth,
via_link,
path_weight,
});
if results.len() >= limit {
break;
}
if depth >= max_depth {
continue;
}
let links = get_neighbors(store, ctx, ¤t, &options.direction).await?;
for link in links.into_iter().rev() {
if !matches_link_type(&link, &options.link_types) {
continue;
}
let edge_weight = get_edge_weight(&link);
if edge_weight < min_weight {
continue;
}
let neighbor = get_neighbor_entity(&link, ¤t, &options.direction);
if visited.contains(&neighbor) {
continue;
}
let new_weight = path_weight + edge_weight;
stack.push((neighbor, depth + 1, new_weight, Some(link)));
}
}
Ok(results)
}