1use crate::key::{EdgeType, NodeId, UrlKey};
10
11pub trait LendingIterator {
13 type Item<'a>
15 where
16 Self: 'a;
17 fn next(&mut self) -> Option<Self::Item<'_>>;
19}
20
21#[derive(Clone, Copy, Debug)]
23pub struct EdgeRef<'a> {
24 pub dst: Option<NodeId>,
26 pub dst_key: UrlKey,
28 pub dst_url: Option<&'a str>,
31 pub etype: EdgeType,
33 pub flags: u8,
35 pub weight: u16,
37}
38
39impl EdgeRef<'_> {
40 #[must_use]
42 pub fn is_inferred(&self) -> bool {
43 self.flags & crate::format::base::eflags::INFERRED != 0
44 }
45}
46
47#[derive(Debug)]
51pub struct Neighbors<'s> {
52 pub(crate) inner: NeighborsInner<'s>,
53}
54
55#[derive(Debug)]
56pub(crate) enum NeighborsInner<'s> {
57 Base { snap: &'s crate::snapshot::Snapshot<'s>, edges: &'s [u8], idx: usize, end: usize },
59 Overlay {
61 snap: &'s crate::snapshot::Snapshot<'s>,
62 edges: &'s [crate::format::wal::OwnedEdge],
63 idx: usize,
64 },
65 Empty,
67}
68
69impl LendingIterator for Neighbors<'_> {
70 type Item<'a>
71 = EdgeRef<'a>
72 where
73 Self: 'a;
74
75 fn next(&mut self) -> Option<EdgeRef<'_>> {
76 match &mut self.inner {
77 NeighborsInner::Base { snap, edges, idx, end } => {
78 if idx >= end {
79 return None;
80 }
81 let rec = &edges[*idx * crate::format::base::EDGE_LEN
82 ..(*idx + 1) * crate::format::base::EDGE_LEN];
83 *idx += 1;
84 let dst = NodeId(crate::bytesio::get_u32(rec, 0));
85 let (dst_key, dst_url) = snap.node_identity(dst);
86 Some(EdgeRef {
87 dst: Some(dst),
88 dst_key,
89 dst_url,
90 etype: EdgeType::from_tag(rec[4]),
91 flags: rec[5],
92 weight: crate::bytesio::get_u16(rec, 6),
93 })
94 }
95 NeighborsInner::Overlay { snap, edges, idx } => {
96 let (dst_key, etype, flags, weight) = *edges.get(*idx)?;
97 *idx += 1;
98 let dst = snap.base_node_id(dst_key);
99 let dst_url = dst.and_then(|id| snap.node_identity(id).1);
100 Some(EdgeRef {
101 dst,
102 dst_key,
103 dst_url,
104 etype: EdgeType::from_tag(etype),
105 flags,
106 weight,
107 })
108 }
109 NeighborsInner::Empty => None,
110 }
111 }
112}
113
114#[derive(Clone, Copy, Debug, PartialEq, Eq)]
116pub enum Direction {
117 Out,
120 In,
123}
124
125#[derive(Clone, Copy, Debug, PartialEq, Eq)]
127pub struct Hop {
128 pub key: UrlKey,
130 pub from: UrlKey,
132 pub etype: EdgeType,
134 pub depth: u32,
136 pub exact_chain: bool,
138}
139
140#[derive(Debug, Default)]
143pub struct Walk {
144 seen: std::collections::HashSet<u64>,
145 frontier: Vec<(UrlKey, bool)>,
146 next: Vec<(UrlKey, bool)>,
147}
148
149impl Walk {
150 #[must_use]
152 pub fn new() -> Self {
153 Self::default()
154 }
155}
156
157impl crate::Snapshot<'_> {
158 pub fn walk(
164 &self,
165 from: UrlKey,
166 dir: Direction,
167 max_depth: u32,
168 follow: impl Fn(EdgeType) -> bool,
169 w: &mut Walk,
170 visit: impl FnMut(Hop) -> bool,
171 ) {
172 let out = dir == Direction::Out;
173 self.walk_along(from, out, !out, max_depth, follow, w, visit);
174 }
175
176 pub fn walk_both(
182 &self,
183 from: UrlKey,
184 max_depth: u32,
185 follow: impl Fn(EdgeType) -> bool,
186 w: &mut Walk,
187 visit: impl FnMut(Hop) -> bool,
188 ) {
189 self.walk_along(from, true, true, max_depth, follow, w, visit);
190 }
191
192 #[allow(clippy::too_many_arguments, clippy::fn_params_excessive_bools)] fn walk_along(
194 &self,
195 from: UrlKey,
196 out: bool,
197 inbound: bool,
198 max_depth: u32,
199 follow: impl Fn(EdgeType) -> bool,
200 w: &mut Walk,
201 mut visit: impl FnMut(Hop) -> bool,
202 ) {
203 w.seen.clear();
204 w.frontier.clear();
205 w.seen.insert(from.0);
206 w.frontier.push((from, true));
207 let exact = crate::format::base::eflags::EXACT;
208 for depth in 1..=max_depth {
209 w.next.clear();
210 for k in 0..w.frontier.len() {
211 let (node, chain) = w.frontier[k];
212 let mut step = |key: UrlKey, etype: EdgeType, flags: u8, w: &mut Walk| {
213 if !follow(etype) || !w.seen.insert(key.0) {
214 return;
215 }
216 let hop = Hop {
217 key,
218 from: node,
219 etype,
220 depth,
221 exact_chain: chain && flags & exact != 0,
222 };
223 if visit(hop) {
224 w.next.push((key, hop.exact_chain));
225 }
226 };
227 if out {
228 let mut it = self.neighbors(node);
229 while let Some(e) = it.next() {
230 step(e.dst_key, e.etype, e.flags, w);
231 }
232 }
233 if inbound {
234 self.in_edges(node, |src, etype, flags, _| step(src, etype, flags, w));
235 }
236 }
237 if w.next.is_empty() {
238 break;
239 }
240 std::mem::swap(&mut w.frontier, &mut w.next);
241 }
242 }
243}