use std::collections::HashMap;
use anyhow::{ensure, Context, Result};
use log::warn;
use crate::graph::*;
use crate::labels::{EdgeLabel, FilenameId, Permission};
use crate::properties;
use crate::NodeType;
fn msg_no_filename_id(name: impl AsRef<[u8]>) -> String {
format!(
"no filename id found for entry \"{}\"",
String::from_utf8_lossy(name.as_ref())
)
}
pub fn fs_resolve_name<G>(graph: &G, dir: NodeId, name: impl AsRef<[u8]>) -> Result<Option<NodeId>>
where
G: SwhLabeledForwardGraph + SwhGraphWithProperties,
<G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
<G as SwhGraphWithProperties>::Maps: properties::Maps,
{
let props = graph.properties();
let name_id = props
.label_name_id(name.as_ref())
.with_context(|| msg_no_filename_id(name))?;
fs_resolve_name_by_id(&graph, dir, name_id)
}
pub fn fs_resolve_name_by_id<G>(graph: &G, dir: NodeId, name: FilenameId) -> Result<Option<NodeId>>
where
G: SwhLabeledForwardGraph + SwhGraphWithProperties,
<G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
<G as SwhGraphWithProperties>::Maps: properties::Maps,
{
let node_type = graph.properties().node_type(dir);
ensure!(
node_type == NodeType::Directory,
"Type of {dir} should be dir, but is {node_type} instead"
);
for (succ, label) in graph.labeled_successors(dir).flatten_labels() {
if let EdgeLabel::DirEntry(dentry) = label {
if dentry.filename_id() == name {
return Ok(Some(succ));
}
}
}
Ok(None)
}
pub fn fs_resolve_path<G>(graph: &G, dir: NodeId, path: impl AsRef<[u8]>) -> Result<Option<NodeId>>
where
G: SwhLabeledForwardGraph + SwhGraphWithProperties,
<G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
<G as SwhGraphWithProperties>::Maps: properties::Maps,
{
let props = graph.properties();
let path = path
.as_ref()
.split(|byte| *byte == b'/')
.map(|name| {
props
.label_name_id(name)
.with_context(|| msg_no_filename_id(name))
})
.collect::<Result<Vec<FilenameId>, _>>()?;
fs_resolve_path_by_id(&graph, dir, &path)
}
pub fn fs_resolve_path_by_id<G>(
graph: &G,
dir: NodeId,
path: &[FilenameId],
) -> Result<Option<NodeId>>
where
G: SwhLabeledForwardGraph + SwhGraphWithProperties,
<G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
<G as SwhGraphWithProperties>::Maps: properties::Maps,
{
let mut cur_entry = dir;
for name in path {
match fs_resolve_name_by_id(graph, cur_entry, *name)? {
None => return Ok(None),
Some(entry) => cur_entry = entry,
}
}
Ok(Some(cur_entry))
}
#[derive(Debug, Default, PartialEq)]
pub enum FsTree {
#[default]
Content,
Directory(HashMap<Vec<u8>, (FsTree, Option<Permission>)>),
Revision(NodeId),
}
pub fn fs_ls_tree<G>(graph: &G, dir: NodeId) -> Result<FsTree>
where
G: SwhLabeledForwardGraph + SwhGraphWithProperties,
<G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
<G as SwhGraphWithProperties>::Maps: properties::Maps,
{
let props = graph.properties();
let node_type = props.node_type(dir);
ensure!(
node_type == NodeType::Directory,
"Type of {dir} should be dir, but is {node_type} instead"
);
let mut dir_entries = HashMap::new();
for (succ, labels) in graph.labeled_successors(dir) {
let node_type = props.node_type(succ);
for label in labels {
if let EdgeLabel::DirEntry(dentry) = label {
let file_name = props.label_name(dentry.filename_id());
let perm = dentry.permission();
match node_type {
NodeType::Content => {
dir_entries.insert(file_name, (FsTree::Content, perm));
}
NodeType::Directory => {
if let Ok(subdir) = fs_ls_tree(graph, succ) {
dir_entries.insert(file_name, (subdir, perm));
} else {
warn!("Cannot list (sub-)directory {succ}, skipping it");
}
}
NodeType::Revision | NodeType::Release => {
dir_entries.insert(file_name, (FsTree::Revision(succ), perm));
}
NodeType::Origin | NodeType::Snapshot => {
warn!("Ignoring dir entry with unexpected type {node_type}");
}
}
}
}
}
Ok(FsTree::Directory(dir_entries))
}