1use std::collections::HashMap;
9
10use anyhow::{ensure, Context, Result};
11use log::warn;
12
13use swh_graph::graph::*;
14use swh_graph::labels::{EdgeLabel, LabelNameId, Permission};
15use swh_graph::properties;
16use swh_graph::NodeType;
17
18fn msg_no_label_name_id(name: impl AsRef<[u8]>) -> String {
19 format!(
20 "no label_name id found for entry \"{}\"",
21 String::from_utf8_lossy(name.as_ref())
22 )
23}
24
25pub fn fs_resolve_name<G>(graph: &G, dir: NodeId, name: impl AsRef<[u8]>) -> Result<Option<NodeId>>
37where
38 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
39 <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
40 <G as SwhGraphWithProperties>::Maps: properties::Maps,
41{
42 let props = graph.properties();
43 let name_id = props
44 .label_name_id(name.as_ref())
45 .with_context(|| msg_no_label_name_id(name))?;
46 fs_resolve_name_by_id(&graph, dir, name_id)
47}
48
49pub fn fs_resolve_name_by_id<G>(graph: &G, dir: NodeId, name: LabelNameId) -> Result<Option<NodeId>>
53where
54 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
55 <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
56 <G as SwhGraphWithProperties>::Maps: properties::Maps,
57{
58 let node_type = graph.properties().node_type(dir);
59 ensure!(
60 node_type == NodeType::Directory,
61 "Type of {dir} should be dir, but is {node_type} instead"
62 );
63
64 for (succ, label) in graph.labeled_successors(dir).flatten_labels() {
65 if let EdgeLabel::DirEntry(dentry) = label {
66 if dentry.label_name_id() == name {
67 return Ok(Some(succ));
68 }
69 }
70 }
71 Ok(None)
72}
73
74pub fn fs_resolve_path<G>(graph: &G, dir: NodeId, path: impl AsRef<[u8]>) -> Result<Option<NodeId>>
87where
88 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
89 <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
90 <G as SwhGraphWithProperties>::Maps: properties::Maps,
91{
92 let props = graph.properties();
93 let path = path
94 .as_ref()
95 .split(|byte| *byte == b'/')
96 .map(|name| {
97 props
98 .label_name_id(name)
99 .with_context(|| msg_no_label_name_id(name))
100 })
101 .collect::<Result<Vec<LabelNameId>, _>>()?;
102 fs_resolve_path_by_id(&graph, dir, &path)
103}
104
105pub fn fs_resolve_path_by_id<G>(
109 graph: &G,
110 dir: NodeId,
111 path: &[LabelNameId],
112) -> Result<Option<NodeId>>
113where
114 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
115 <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
116 <G as SwhGraphWithProperties>::Maps: properties::Maps,
117{
118 let mut cur_entry = dir;
119 for name in path {
120 match fs_resolve_name_by_id(graph, cur_entry, *name)? {
121 None => return Ok(None),
122 Some(entry) => cur_entry = entry,
123 }
124 }
125 Ok(Some(cur_entry))
126}
127
128#[derive(Debug, Default, PartialEq)]
133pub enum FsTree {
134 #[default]
135 Content,
136 Directory(HashMap<Vec<u8>, (FsTree, Option<Permission>)>),
137 Revision(NodeId),
138}
139
140pub fn fs_ls_tree<G>(graph: &G, dir: NodeId) -> Result<FsTree>
148where
149 G: SwhLabeledForwardGraph + SwhGraphWithProperties,
150 <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
151 <G as SwhGraphWithProperties>::Maps: properties::Maps,
152{
153 let props = graph.properties();
154 let node_type = props.node_type(dir);
155 ensure!(
156 node_type == NodeType::Directory,
157 "Type of {dir} should be dir, but is {node_type} instead"
158 );
159
160 let mut dir_entries = HashMap::new();
161 for (succ, labels) in graph.labeled_successors(dir) {
162 let node_type = props.node_type(succ);
163 for label in labels {
164 if let EdgeLabel::DirEntry(dentry) = label {
165 let file_name = props.label_name(dentry.label_name_id());
166 let perm = dentry.permission();
167 match node_type {
168 NodeType::Content => {
169 dir_entries.insert(file_name, (FsTree::Content, perm));
170 }
171 NodeType::Directory => {
172 if let Ok(subdir) = fs_ls_tree(graph, succ) {
174 dir_entries.insert(file_name, (subdir, perm));
175 } else {
176 warn!("Cannot list (sub-)directory {succ}, skipping it");
177 }
178 }
179 NodeType::Revision | NodeType::Release => {
180 dir_entries.insert(file_name, (FsTree::Revision(succ), perm));
181 }
182 NodeType::Origin | NodeType::Snapshot => {
183 warn!("Ignoring dir entry with unexpected type {node_type}");
184 }
185 }
186 }
187 }
188 }
189
190 Ok(FsTree::Directory(dir_entries))
191}