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