1use anyhow::{Result, bail, ensure};
9
10use swh_graph::graph::*;
11use swh_graph::labels::{EdgeLabel, VisitStatus};
12use swh_graph::{NodeConstraint, NodeType, properties};
13
14pub fn find_latest_snp<G>(graph: &G, ori: NodeId) -> Result<Option<(NodeId, u64)>>
21where
22 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
23 <G as SwhGraphWithProperties>::Maps: properties::Maps,
24{
25 let props = graph.properties();
26 let node_type = props.node_type(ori);
27 ensure!(
28 node_type == NodeType::Origin,
29 "Cannot find latest snapshot of {}: not an origin",
30 graph.properties().swhid(ori),
31 );
32 let mut latest_snp: Option<(usize, u64)> = None;
34 for (succ, labels) in graph.labeled_successors(ori) {
35 let node_type = props.node_type(succ);
36 if node_type != NodeType::Snapshot {
37 continue;
38 }
39 for label in labels {
40 if let EdgeLabel::Visit(visit) = label {
41 if visit.status() != VisitStatus::Full {
42 continue;
43 }
44 let ts = visit.timestamp();
45 if let Some((_cur_snp, cur_ts)) = latest_snp {
46 if ts > cur_ts {
47 latest_snp = Some((succ, ts))
48 }
49 } else {
50 latest_snp = Some((succ, ts))
51 }
52 }
53 }
54 }
55 Ok(latest_snp)
56}
57
58pub fn peel_once<G>(graph: &G, node: NodeId) -> Result<Option<NodeId>>
75where
76 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
77 <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
78 <G as SwhGraphWithProperties>::Maps: properties::Maps,
79{
80 ensure!(!graph.is_transposed(), "Cannot peel on a transposed graph.");
81 let props = graph.properties();
82 let source_type = props.node_type(node);
83
84 match source_type {
85 NodeType::Content | NodeType::Directory => {
86 bail!("Cannot peel nodes of type {source_type}");
87 }
88 NodeType::Revision => {
89 for succ in graph.successors(node) {
90 if props.node_type(succ) == NodeType::Directory {
91 return Ok(Some(succ));
92 }
93 }
94 Ok(None)
95 }
96 NodeType::Release => {
97 if let Some(succ) = graph.successors(node).into_iter().next() {
98 return Ok(Some(succ));
99 }
100 Ok(None)
101 }
102 NodeType::Snapshot => {
103 if let Ok(Some(succ)) = vcs::find_head_rev(&graph, node) {
104 Ok(Some(succ))
105 } else {
106 for succ in graph.successors(node) {
107 let t = props.node_type(succ);
108 if t == NodeType::Revision || t == NodeType::Release {
109 return Ok(Some(succ));
110 }
111 }
112 Ok(None)
113 }
114 }
115 NodeType::Origin => {
116 find_latest_snp(&graph, node).map(|opt_node| opt_node.map(|(node, _ts)| node))
117 }
118 }
119}
120
121pub fn peel_rel<G>(graph: &G, node: NodeId) -> Result<Option<NodeId>>
135where
136 G: SwhForwardGraph + SwhGraphWithProperties,
137 <G as SwhGraphWithProperties>::Maps: properties::Maps,
138{
139 ensure!(!graph.is_transposed(), "Cannot peel on a transposed graph.");
140 let props = graph.properties();
141
142 let mut current = node;
143 while props.node_type(current) == NodeType::Release {
144 match graph.successors(current).into_iter().next() {
145 Some(succ) => current = succ,
146 None => return Ok(None),
147 }
148 }
149
150 Ok(Some(current))
151}
152
153pub fn peel<G>(graph: &G, node: NodeId) -> Result<Option<NodeId>>
164where
165 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
166 <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
167 <G as SwhGraphWithProperties>::Maps: properties::Maps,
168{
169 let props = graph.properties();
170 let source_type = props.node_type(node);
171
172 let mut current = node;
173 while props.node_type(current) == source_type {
174 match peel_once(graph, current)? {
175 Some(next) => current = next,
176 None => return Ok(None),
177 }
178 }
179
180 Ok(Some(current))
181}
182
183pub fn peel_until<G>(graph: &G, node: NodeId, target_type: NodeConstraint) -> Result<Option<NodeId>>
196where
197 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
198 <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
199 <G as SwhGraphWithProperties>::Maps: properties::Maps,
200{
201 let props = graph.properties();
202 let source_type = props.node_type(node);
203
204 if target_type.matches(source_type) {
206 return Ok(Some(node));
207 }
208
209 let Some(mut current) = peel_once(graph, node)? else {
211 return Ok(None);
212 };
213
214 loop {
215 let current_type = props.node_type(current);
216 if target_type.matches(current_type) {
217 return Ok(Some(current));
218 }
219 if current_type == NodeType::Release || current_type == NodeType::Revision {
220 match peel_once(graph, current)? {
222 Some(next) => current = next,
223 None => return Ok(None),
224 }
225 } else {
226 bail!("Cannot peel to {target_type}: stuck at node {current} of type {current_type}");
227 }
228 }
229}
230
231pub mod collections;
232pub mod connectivity;
233pub mod diff;
234pub mod fs;
235pub mod io;
236pub mod labeling;
237pub mod origins;
238#[cfg(feature = "unstable_project_ids")]
239pub mod project_ids;
240mod root_directory;
241pub use root_directory::find_root_dir;
242pub mod vcs;
243mod visit;
244pub use visit::*;
245
246pub use deprecated::*;
247
248#[allow(deprecated)]
249mod deprecated {
250 use std::collections::HashMap;
251 use swh_graph::labels::{LabelNameId, Permission};
252
253 use super::*;
254
255 #[doc(hidden)]
256 #[deprecated(since = "10.3.0", note = "use swh_graph_stdlib::vcs::HEAD_REF_NAMES")]
257 pub const HEAD_REF_NAMES: [&str; 2] = vcs::HEAD_REF_NAMES;
258
259 #[doc(hidden)]
260 #[deprecated(since = "10.3.0", note = "use swh_graph_stdlib::vcs::find_head_rev")]
261 pub fn find_head_rev<G>(graph: &G, snp: NodeId) -> Result<Option<NodeId>>
262 where
263 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
264 <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
265 <G as SwhGraphWithProperties>::Maps: properties::Maps,
266 {
267 vcs::find_head_rev(graph, snp)
268 }
269
270 #[doc(hidden)]
271 #[deprecated(
272 since = "10.3.0",
273 note = "use swh_graph_stdlib::vcs::find_head_rev_by_refs"
274 )]
275 pub fn find_head_rev_by_refs<G>(
276 graph: &G,
277 snp: NodeId,
278 ref_name_ids: &[LabelNameId],
279 ) -> Result<Option<NodeId>>
280 where
281 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
282 <G as SwhGraphWithProperties>::Maps: properties::Maps,
283 <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
284 {
285 vcs::find_head_rev_by_refs(graph, snp, ref_name_ids)
286 }
287
288 #[doc(hidden)]
289 #[deprecated(since = "10.3.0", note = "use swh_graph_stdlib::fs::FsTree")]
290 #[derive(Debug, Default, PartialEq)]
291 pub enum FsTree {
292 #[default]
293 Content,
294 Directory(HashMap<Vec<u8>, (FsTree, Option<Permission>)>),
295 Revision(NodeId),
296 }
297
298 impl From<fs::FsTree> for FsTree {
299 fn from(tree: fs::FsTree) -> Self {
300 match tree {
301 fs::FsTree::Content => FsTree::Content,
302 fs::FsTree::Directory(entries) => FsTree::Directory(
303 entries
304 .into_iter()
305 .map(|(name, (tree, perm))| (name, (tree.into(), perm)))
306 .collect(),
307 ),
308 fs::FsTree::Revision(rev) => FsTree::Revision(rev),
309 }
310 }
311 }
312
313 #[doc(hidden)]
314 #[deprecated(since = "10.3.0", note = "use swh_graph_stdlib::fs::resolve_name")]
315 pub fn fs_resolve_name<G>(
316 graph: &G,
317 dir: NodeId,
318 name: impl AsRef<[u8]>,
319 ) -> Result<Option<NodeId>>
320 where
321 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
322 <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
323 <G as SwhGraphWithProperties>::Maps: properties::Maps,
324 {
325 fs::resolve_name(graph, dir, name)
326 }
327
328 pub fn fs_resolve_name_by_id<G>(
329 graph: &G,
330 dir: NodeId,
331 name: LabelNameId,
332 ) -> Result<Option<NodeId>>
333 where
334 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
335 <G as SwhGraphWithProperties>::Maps: properties::Maps,
336 {
337 fs::resolve_name_by_id(graph, dir, name)
338 }
339
340 #[doc(hidden)]
341 #[deprecated(since = "10.3.0", note = "use swh_graph_stdlib::fs::resolve_path")]
342 pub fn fs_resolve_path<G>(
343 graph: &G,
344 dir: NodeId,
345 path: impl AsRef<[u8]>,
346 ) -> Result<Option<NodeId>>
347 where
348 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
349 <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
350 <G as SwhGraphWithProperties>::Maps: properties::Maps,
351 {
352 fs::resolve_path(graph, dir, path)
353 }
354
355 #[doc(hidden)]
356 #[deprecated(
357 since = "10.3.0",
358 note = "use swh_graph_stdlib::fs::resolve_path_by_id"
359 )]
360 pub fn fs_resolve_path_by_id<G>(
361 graph: &G,
362 dir: NodeId,
363 path: &[LabelNameId],
364 ) -> Result<Option<NodeId>>
365 where
366 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
367 <G as SwhGraphWithProperties>::Maps: properties::Maps,
368 {
369 fs::resolve_path_by_id(graph, dir, path)
370 }
371
372 #[doc(hidden)]
373 #[deprecated(since = "10.3.0", note = "use swh_graph_stdlib::fs::ls_tree")]
374 pub fn fs_ls_tree<G>(graph: &G, dir: NodeId) -> Result<FsTree>
375 where
376 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
377 <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
378 <G as SwhGraphWithProperties>::Maps: properties::Maps,
379 {
380 fs::ls_tree(graph, dir).map(Into::into)
381 }
382}