1use std::collections::{BTreeSet, HashMap, HashSet, VecDeque};
5
6use sinter_core::{Confidence, Edge, Evidence, Node, NodeId, Relation};
7
8use crate::error::StoreError;
9use crate::store::Store;
10
11#[derive(Debug, Default, Clone)]
14pub struct EdgeFilter {
15 pub evidence: Option<BTreeSet<Evidence>>,
17 pub min_confidence: Option<Confidence>,
19}
20
21impl EdgeFilter {
22 pub fn admits(&self, edge: &Edge) -> bool {
23 if edge.relation == Relation::Contains {
24 return false;
25 }
26 if let Some(allowed) = &self.evidence
27 && !allowed.contains(&edge.evidence)
28 {
29 return false;
30 }
31 if self.min_confidence == Some(Confidence::Certain)
32 && edge.confidence != Confidence::Certain
33 {
34 return false;
35 }
36 true
37 }
38}
39
40pub struct Reached {
43 pub node: Node,
44 pub depth: usize,
45 pub via: Edge,
46}
47
48impl Store {
49 pub fn dependents(
52 &self,
53 id: &NodeId,
54 filter: &EdgeFilter,
55 max_depth: usize,
56 ) -> Result<Vec<Reached>, StoreError> {
57 let mut seen: HashSet<NodeId> = HashSet::from([id.clone()]);
58 let mut queue: VecDeque<(NodeId, usize)> = VecDeque::from([(id.clone(), 0)]);
59 let mut out = Vec::new();
60 while let Some((current, depth)) = queue.pop_front() {
61 if depth >= max_depth {
62 continue;
63 }
64 for edge in self.in_edges(¤t)? {
65 if !filter.admits(&edge) || !seen.insert(edge.src.clone()) {
66 continue;
67 }
68 if let Some(node) = self.node(&edge.src)? {
69 queue.push_back((edge.src.clone(), depth + 1));
70 out.push(Reached {
71 node,
72 depth: depth + 1,
73 via: edge,
74 });
75 }
76 }
77 }
78 Ok(out)
79 }
80
81 pub fn shortest_path(
83 &self,
84 from: &NodeId,
85 to: &NodeId,
86 filter: &EdgeFilter,
87 ) -> Result<Option<Vec<Edge>>, StoreError> {
88 let mut prev: HashMap<NodeId, Edge> = HashMap::new();
89 let mut seen: HashSet<NodeId> = HashSet::from([from.clone()]);
90 let mut queue: VecDeque<NodeId> = VecDeque::from([from.clone()]);
91 if self
95 .node(from)?
96 .is_some_and(|n| n.kind == sinter_core::SymbolKind::File)
97 {
98 for edge in self.out_edges(from)? {
99 if edge.relation == Relation::Contains && seen.insert(edge.dst.clone()) {
100 prev.insert(edge.dst.clone(), edge.clone());
101 queue.push_back(edge.dst.clone());
102 }
103 }
104 }
105 while let Some(current) = queue.pop_front() {
106 if ¤t == to {
107 let mut path = Vec::new();
108 let mut at = to.clone();
109 while &at != from {
110 let edge = prev[&at].clone();
111 at = edge.src.clone();
112 path.push(edge);
113 }
114 path.reverse();
115 return Ok(Some(path));
116 }
117 for edge in self.out_edges(¤t)? {
118 if !filter.admits(&edge) || !seen.insert(edge.dst.clone()) {
119 continue;
120 }
121 prev.insert(edge.dst.clone(), edge.clone());
122 queue.push_back(edge.dst.clone());
123 }
124 }
125 Ok(None)
126 }
127}