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<G>(graph: &G, node: NodeId) -> Result<Option<NodeId>>
132where
133 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
134 <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
135 <G as SwhGraphWithProperties>::Maps: properties::Maps,
136{
137 let props = graph.properties();
138 let source_type = props.node_type(node);
139
140 let mut current = node;
141 while props.node_type(current) == source_type {
142 match peel_once(graph, current)? {
143 Some(next) => current = next,
144 None => return Ok(None),
145 }
146 }
147
148 Ok(Some(current))
149}
150
151pub fn peel_until<G>(graph: &G, node: NodeId, target_type: NodeConstraint) -> 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 if target_type.matches(source_type) {
174 return Ok(Some(node));
175 }
176
177 let Some(mut current) = peel_once(graph, node)? else {
179 return Ok(None);
180 };
181
182 loop {
183 let current_type = props.node_type(current);
184 if target_type.matches(current_type) {
185 return Ok(Some(current));
186 }
187 if current_type == NodeType::Release || current_type == NodeType::Revision {
188 match peel_once(graph, current)? {
190 Some(next) => current = next,
191 None => return Ok(None),
192 }
193 } else {
194 bail!("Cannot peel to {target_type}: stuck at node {current} of type {current_type}");
195 }
196 }
197}
198
199pub mod collections;
200pub mod connectivity;
201pub mod diff;
202pub mod fs;
203pub mod io;
204pub mod labeling;
205pub mod origins;
206#[cfg(feature = "unstable_project_ids")]
207pub mod project_ids;
208mod root_directory;
209pub use root_directory::find_root_dir;
210pub mod vcs;
211mod visit;
212pub use visit::*;
213
214pub use deprecated::*;
215
216#[allow(deprecated)]
217mod deprecated {
218 use std::collections::HashMap;
219 use swh_graph::labels::{LabelNameId, Permission};
220
221 use super::*;
222
223 #[doc(hidden)]
224 #[deprecated(since = "10.3.0", note = "use swh_graph_stdlib::vcs::HEAD_REF_NAMES")]
225 pub const HEAD_REF_NAMES: [&str; 2] = vcs::HEAD_REF_NAMES;
226
227 #[doc(hidden)]
228 #[deprecated(since = "10.3.0", note = "use swh_graph_stdlib::vcs::find_head_rev")]
229 pub fn find_head_rev<G>(graph: &G, snp: NodeId) -> Result<Option<NodeId>>
230 where
231 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
232 <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
233 <G as SwhGraphWithProperties>::Maps: properties::Maps,
234 {
235 vcs::find_head_rev(graph, snp)
236 }
237
238 #[doc(hidden)]
239 #[deprecated(
240 since = "10.3.0",
241 note = "use swh_graph_stdlib::vcs::find_head_rev_by_refs"
242 )]
243 pub fn find_head_rev_by_refs<G>(
244 graph: &G,
245 snp: NodeId,
246 ref_name_ids: &[LabelNameId],
247 ) -> Result<Option<NodeId>>
248 where
249 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
250 <G as SwhGraphWithProperties>::Maps: properties::Maps,
251 <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
252 {
253 vcs::find_head_rev_by_refs(graph, snp, ref_name_ids)
254 }
255
256 #[doc(hidden)]
257 #[deprecated(since = "10.3.0", note = "use swh_graph_stdlib::fs::FsTree")]
258 #[derive(Debug, Default, PartialEq)]
259 pub enum FsTree {
260 #[default]
261 Content,
262 Directory(HashMap<Vec<u8>, (FsTree, Option<Permission>)>),
263 Revision(NodeId),
264 }
265
266 impl From<fs::FsTree> for FsTree {
267 fn from(tree: fs::FsTree) -> Self {
268 match tree {
269 fs::FsTree::Content => FsTree::Content,
270 fs::FsTree::Directory(entries) => FsTree::Directory(
271 entries
272 .into_iter()
273 .map(|(name, (tree, perm))| (name, (tree.into(), perm)))
274 .collect(),
275 ),
276 fs::FsTree::Revision(rev) => FsTree::Revision(rev),
277 }
278 }
279 }
280
281 #[doc(hidden)]
282 #[deprecated(since = "10.3.0", note = "use swh_graph_stdlib::fs::resolve_name")]
283 pub fn fs_resolve_name<G>(
284 graph: &G,
285 dir: NodeId,
286 name: impl AsRef<[u8]>,
287 ) -> Result<Option<NodeId>>
288 where
289 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
290 <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
291 <G as SwhGraphWithProperties>::Maps: properties::Maps,
292 {
293 fs::resolve_name(graph, dir, name)
294 }
295
296 pub fn fs_resolve_name_by_id<G>(
297 graph: &G,
298 dir: NodeId,
299 name: LabelNameId,
300 ) -> Result<Option<NodeId>>
301 where
302 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
303 <G as SwhGraphWithProperties>::Maps: properties::Maps,
304 {
305 fs::resolve_name_by_id(graph, dir, name)
306 }
307
308 #[doc(hidden)]
309 #[deprecated(since = "10.3.0", note = "use swh_graph_stdlib::fs::resolve_path")]
310 pub fn fs_resolve_path<G>(
311 graph: &G,
312 dir: NodeId,
313 path: impl AsRef<[u8]>,
314 ) -> Result<Option<NodeId>>
315 where
316 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
317 <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
318 <G as SwhGraphWithProperties>::Maps: properties::Maps,
319 {
320 fs::resolve_path(graph, dir, path)
321 }
322
323 #[doc(hidden)]
324 #[deprecated(
325 since = "10.3.0",
326 note = "use swh_graph_stdlib::fs::resolve_path_by_id"
327 )]
328 pub fn fs_resolve_path_by_id<G>(
329 graph: &G,
330 dir: NodeId,
331 path: &[LabelNameId],
332 ) -> Result<Option<NodeId>>
333 where
334 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
335 <G as SwhGraphWithProperties>::Maps: properties::Maps,
336 {
337 fs::resolve_path_by_id(graph, dir, path)
338 }
339
340 #[doc(hidden)]
341 #[deprecated(since = "10.3.0", note = "use swh_graph_stdlib::fs::ls_tree")]
342 pub fn fs_ls_tree<G>(graph: &G, dir: NodeId) -> Result<FsTree>
343 where
344 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
345 <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
346 <G as SwhGraphWithProperties>::Maps: properties::Maps,
347 {
348 fs::ls_tree(graph, dir).map(Into::into)
349 }
350}