use std::{
borrow::Cow,
path::{Path, PathBuf},
};
use either::Either::{self, Left, Right};
pub struct Entry<'a, V> {
pub path: PathBuf,
pub data: &'a V,
}
impl<'a, V> Entry<'a, V> {
#[inline(always)]
pub fn into_path(self) -> PathBuf {
self.path
}
#[inline(always)]
pub fn into_value(self) -> &'a V {
self.data
}
}
impl<'a, V> AsRef<Path> for Entry<'a, V> {
#[inline(always)]
fn as_ref(&self) -> &Path {
self.path.as_path()
}
}
impl<'a, V> AsRef<V> for Entry<'a, V> {
#[inline(always)]
fn as_ref(&self) -> &V {
self.data
}
}
#[derive(Debug, thiserror::Error)]
pub enum TryInsertError {
#[error("unable to insert path due to existing entry with conflicting file type")]
PathTypeConflict,
#[error("the path '{}' is unreachable: an ancestor of this path is a file", .0.display())]
Unreachable(PathBuf),
}
pub struct PathPrefixTree<V> {
tree: PrefixTree,
data: Vec<V>,
}
impl<V> Default for PathPrefixTree<V> {
fn default() -> Self {
Self {
tree: PrefixTree::default(),
data: vec![],
}
}
}
impl<V: Clone> Clone for PathPrefixTree<V> {
fn clone(&self) -> Self {
let tree = self.tree.clone();
let data = self.data.clone();
Self { tree, data }
}
}
impl<'a, V> FromIterator<&'a str> for PathPrefixTree<V>
where
V: Default,
{
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = &'a str>,
{
let mut tree = Self::default();
for path in iter {
tree.insert(path, V::default());
}
tree
}
}
impl<'a, V> FromIterator<&'a Path> for PathPrefixTree<V>
where
V: Default,
{
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = &'a Path>,
{
let mut tree = Self::default();
for path in iter {
tree.insert(path, V::default());
}
tree
}
}
impl<V, P> FromIterator<(P, V)> for PathPrefixTree<V>
where
P: AsRef<Path>,
{
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = (P, V)>,
{
let mut tree = Self::default();
for (path, value) in iter {
tree.insert(path.as_ref(), value);
}
tree
}
}
impl<V> PathPrefixTree<V> {
pub fn new() -> Self {
Self::default()
}
pub fn insert<P: AsRef<Path>>(&mut self, path: P, value: V) {
self.try_insert(path, value).expect("insert failed")
}
pub fn try_insert<P: AsRef<Path>>(&mut self, path: P, value: V) -> Result<(), TryInsertError> {
let data = DataKey(self.data.len());
self.data.push(value);
self.tree.insert(path.as_ref(), data)
}
pub fn clear(&mut self) {
self.tree.clear();
self.data.clear();
}
pub fn contains<P: AsRef<Path>>(&self, path: P) -> bool {
self.tree.contains(path.as_ref())
}
pub fn get<P: AsRef<Path>>(&self, path: P) -> Option<&V> {
let key = self.tree.get(path.as_ref())?;
Some(&self.data[key.as_usize()])
}
pub fn get_mut<P: AsRef<Path>>(&mut self, path: P) -> Option<&mut V> {
let key = self.tree.get(path.as_ref())?;
Some(&mut self.data[key.as_usize()])
}
pub fn nearest_ancestor<P: AsRef<Path>>(&self, path: P) -> Option<Entry<'_, V>> {
self.tree
.nearest_ancestor(path.as_ref())
.map(|(path, key)| Entry {
path,
data: &self.data[key.as_usize()],
})
}
pub fn iter(&self) -> impl Iterator<Item = Entry<'_, V>> + '_ {
Dfs::new(self, false, None)
}
pub fn files(&self) -> impl Iterator<Item = Entry<'_, V>> + '_ {
Dfs::new(self, true, None)
}
pub fn children<P: AsRef<Path>>(
&self,
path: P,
max_depth: Option<usize>,
) -> impl Iterator<Item = Entry<'_, V>> + '_ {
Dfs::at(self, path.as_ref(), false, max_depth)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum PathType {
Unknown,
File,
Dir,
}
#[derive(Default, Clone)]
struct PrefixTree {
roots: Vec<Node>,
}
impl PrefixTree {
pub fn insert(&mut self, path: &Path, data: DataKey) -> Result<(), TryInsertError> {
let (path, ty) = canonicalize_and_type(path);
if let Some(sort) = self.try_insert_existing(&path, ty, data)? {
if sort {
self.roots.sort();
}
return Ok(());
}
self.insert_new(path.into_owned(), ty, data);
Ok(())
}
fn try_insert_existing(
&mut self,
path: &Path,
ty: PathType,
data: DataKey,
) -> Result<Option<bool>, TryInsertError> {
for root in self.roots.iter_mut() {
if root.has_common_prefix(path) {
return insert_into_prefix(root, path, ty, data).map(|depth| Some(depth == 0));
}
}
Ok(None)
}
fn insert_new(&mut self, mut path: PathBuf, ty: PathType, data: DataKey) {
let root = match ty {
PathType::Dir => Node::Branch {
component: path,
children: vec![],
data: Some(data),
},
ty @ (PathType::Unknown | PathType::File) => {
let mut components = path.components();
let file = PathBuf::from(components.next_back().unwrap().as_os_str());
let child = Node::Leaf {
component: file,
ty,
data: Some(data),
};
path.pop();
Node::Branch {
component: path,
children: vec![child],
data: None,
}
}
};
self.roots.push(root);
self.roots.sort();
}
pub fn clear(&mut self) {
self.roots.clear();
}
pub fn contains(&self, path: &Path) -> bool {
let (path, _) = canonicalize_and_type(path);
self.roots.iter().any(|root| root.contains(&path))
}
pub fn get(&self, path: &Path) -> Option<DataKey> {
let (path, _) = canonicalize_and_type(path);
self.roots.iter().find_map(|root| {
if root.has_common_prefix(&path) {
root.get(&path).and_then(|n| n.data())
} else {
None
}
})
}
pub fn nearest_ancestor(&self, path: &Path) -> Option<(PathBuf, DataKey)> {
let (path, _) = canonicalize_and_type(path);
self.roots
.iter()
.find_map(|root| root.nearest_ancestor(&path))
}
}
fn canonicalize_and_type<'a>(path: &'a Path) -> (Cow<'a, Path>, PathType) {
use smallvec::SmallVec;
use std::path::Component;
const PATH_SEPARATOR_SIZE: usize = std::path::MAIN_SEPARATOR_STR.len();
if let Ok(path) = path.canonicalize() {
let path: Cow<'a, Path> = Cow::Owned(path);
let ty = match path.metadata() {
Err(_) => PathType::Unknown,
Ok(metadata) => {
if metadata.is_dir() {
PathType::Dir
} else {
PathType::File
}
}
};
return (path, ty);
}
let mut components = SmallVec::<[Component; 4]>::default();
let mut canonical = true;
let mut size_in_bytes = 0;
for component in path.components() {
match component {
component @ (Component::Normal(_) | Component::RootDir | Component::Prefix(_)) => {
size_in_bytes += component.as_os_str().len() + PATH_SEPARATOR_SIZE;
components.push(component);
}
Component::CurDir => {
canonical = false;
}
Component::ParentDir => match components.last() {
None | Some(Component::ParentDir) => {
let component = Component::ParentDir;
size_in_bytes += component.as_os_str().len() + PATH_SEPARATOR_SIZE;
components.push(component);
}
Some(_) => {
canonical = false;
let component = unsafe { components.pop().unwrap_unchecked() };
size_in_bytes -= component.as_os_str().len() + PATH_SEPARATOR_SIZE;
}
},
}
}
if canonical {
return (Cow::Borrowed(path), PathType::Unknown);
}
let mut path = PathBuf::with_capacity(size_in_bytes);
for component in components.into_iter() {
path.push(component);
}
(Cow::Owned(path), PathType::Unknown)
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct DataKey(usize);
impl DataKey {
#[inline(always)]
const fn as_usize(self) -> usize {
self.0
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum Prefix {
Exact,
Ancestor,
Child,
Partial(PathBuf),
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum Node {
Leaf {
component: PathBuf,
ty: PathType,
data: Option<DataKey>,
},
Branch {
component: PathBuf,
children: Vec<Node>,
data: Option<DataKey>,
},
}
impl Ord for Node {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.component().cmp(other.component())
}
}
impl PartialOrd for Node {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Node {
#[inline]
pub fn component(&self) -> &Path {
match self {
Self::Leaf { component, .. } | Self::Branch { component, .. } => component.as_path(),
}
}
#[inline]
pub fn data(&self) -> Option<DataKey> {
match self {
Self::Leaf { data, .. } | Self::Branch { data, .. } => *data,
}
}
pub fn common_prefix(&self, path: &Path) -> Option<Prefix> {
assert_ne!(
path,
Path::new(""),
"invalid empty path given to common_prefix"
);
let mut ac = self.component().components();
let mut bc = path.components();
let mut common_prefix = PathBuf::new();
loop {
match (ac.next(), bc.next()) {
(None, None) => break Some(Prefix::Exact),
(None, Some(_)) => break Some(Prefix::Child),
(Some(a), Some(b)) if a == b => {
common_prefix.push(a);
}
(Some(_), Some(_)) => {
if common_prefix == Path::new("") {
break None;
} else {
break Some(Prefix::Partial(common_prefix));
}
}
(Some(_), None) => {
break Some(Prefix::Ancestor);
}
}
}
}
pub fn has_common_prefix(&self, path: &Path) -> bool {
assert_ne!(
path,
Path::new(""),
"invalid empty path given to has_common_prefix"
);
let mut ac = self.component().components();
let mut bc = path.components();
let mut has_minimum_prefix = false;
loop {
match (ac.next(), bc.next()) {
(None, _) => break true,
(Some(a), Some(b)) if a == b => {
has_minimum_prefix = true;
}
(Some(_), Some(_)) => break has_minimum_prefix,
(Some(_), None) => break true,
}
}
}
pub fn contains(&self, mut path: &Path) -> bool {
let mut next = Some(self);
while let Some(node) = next.take() {
match node {
Self::Branch {
component,
children,
data,
} => {
if let Ok(rest) = path.strip_prefix(component) {
if rest == Path::new("") {
return data.is_some();
}
match children.iter().position(|c| c.has_common_prefix(rest)) {
Some(index) => {
path = rest;
next = Some(&children[index]);
continue;
}
None => {
break;
}
}
} else {
break;
}
}
Self::Leaf {
component, data, ..
} => {
return path == component && data.is_some();
}
}
}
false
}
pub fn get<'a>(&'a self, mut path: &Path) -> Option<&'a Node> {
let mut next = Some(self);
while let Some(node) = next.take() {
match node {
Self::Branch {
component,
children,
..
} => {
if let Ok(rest) = path.strip_prefix(component) {
if rest == Path::new("") {
return Some(node);
}
match children.iter().position(|c| c.has_common_prefix(rest)) {
Some(index) => {
path = rest;
next = Some(&children[index]);
continue;
}
None => {
break;
}
}
}
}
Self::Leaf { component, .. } => {
if path == component {
return Some(node);
}
break;
}
}
}
None
}
pub fn nearest_ancestor(&self, mut path: &Path) -> Option<(PathBuf, DataKey)> {
use smallvec::SmallVec;
if !self.has_common_prefix(path) {
return None;
}
let mut candidates = SmallVec::<[(PathBuf, DataKey); 2]>::default();
let mut ancestor = PathBuf::new();
let mut next = Some(self);
while let Some(node) = next.take() {
match node {
Self::Branch {
component,
children,
data,
} => {
if let Ok(rest) = path.strip_prefix(component) {
if rest == Path::new("") {
return candidates.pop();
}
ancestor.push(component);
let child = children.iter().position(|c| c.has_common_prefix(rest));
if let Some(index) = child {
if let Some(data) = *data {
candidates.push((ancestor.clone(), data));
}
path = rest;
next = Some(&children[index]);
continue;
} else {
return data
.map(|data| (ancestor, data))
.or_else(|| candidates.pop());
}
} else {
return candidates.pop();
}
}
Self::Leaf { .. } => return candidates.pop(),
}
}
None
}
fn take(&mut self, prefix: &Path) -> Self {
match self {
Node::Branch {
component,
children,
data,
} => {
let component = component.strip_prefix(prefix).unwrap().to_path_buf();
Node::Branch {
component,
children: core::mem::take(children),
data: data.take(),
}
}
Node::Leaf {
component,
data,
ty,
} => {
let component = component.strip_prefix(prefix).unwrap().to_path_buf();
Node::Leaf {
component,
ty: *ty,
data: data.take(),
}
}
}
}
fn split_at(&mut self, common_prefix: PathBuf) {
let split = self.take(&common_prefix);
*self = Node::Branch {
component: common_prefix,
children: vec![split],
data: None,
};
}
fn set_type(&mut self, ty: PathType) -> Result<(), TryInsertError> {
match self {
Self::Leaf {
component,
data,
ty: prev_ty,
..
} => match ty {
PathType::Unknown | PathType::File => *prev_ty = ty,
PathType::Dir => {
let component = core::mem::replace(component, PathBuf::new());
let data = data.take();
*self = Self::Branch {
component,
data,
children: vec![],
};
}
},
Self::Branch { .. } => match ty {
PathType::Dir => (),
PathType::Unknown | PathType::File => return Err(TryInsertError::PathTypeConflict),
},
}
Ok(())
}
fn set_data(&mut self, data: DataKey) {
match self {
Self::Branch {
data: prev_data, ..
}
| Self::Leaf {
data: prev_data, ..
} => {
*prev_data = Some(data);
}
}
}
fn push_child(
&mut self,
component: PathBuf,
ty: PathType,
data: Option<DataKey>,
) -> Result<(), TryInsertError> {
let child = match ty {
PathType::File | PathType::Unknown => Self::Leaf {
component,
ty,
data,
},
PathType::Dir => Self::Branch {
component,
children: vec![],
data,
},
};
match self {
Self::Branch { children, .. } => {
children.push(child);
children.sort();
}
Self::Leaf {
component: parent_component,
data: parent_data,
ty: PathType::Unknown,
} => {
let children = vec![child];
let component = core::mem::replace(parent_component, PathBuf::new());
let data = parent_data.take();
*self = Self::Branch {
component,
children,
data,
};
}
Self::Leaf { .. } => return Err(TryInsertError::PathTypeConflict),
}
Ok(())
}
fn try_insert_new<'a>(
&'a mut self,
path: &Path,
component: &Path,
ty: PathType,
data: Option<DataKey>,
) -> Either<Result<(), TryInsertError>, &'a mut Node> {
match self {
Self::Branch { children, .. } => {
if let Some(index) = children.iter().position(|c| c.has_common_prefix(component)) {
return Right(&mut children[index]);
}
let child = match ty {
PathType::File | PathType::Unknown => Self::Leaf {
component: component.to_path_buf(),
ty,
data,
},
PathType::Dir => Self::Branch {
component: component.to_path_buf(),
children: vec![],
data,
},
};
children.push(child);
children.sort();
Left(Ok(()))
}
Self::Leaf {
ty: PathType::File, ..
} => Left(Err(TryInsertError::Unreachable(path.to_path_buf()))),
Self::Leaf {
component: parent_component,
data: parent_data,
..
} => {
let child = match ty {
PathType::File | PathType::Unknown => Self::Leaf {
component: component.to_path_buf(),
ty,
data,
},
PathType::Dir => Self::Branch {
component: component.to_path_buf(),
children: vec![],
data,
},
};
let children = vec![child];
let component = core::mem::replace(parent_component, PathBuf::new());
let data = parent_data.take();
*self = Self::Branch {
component,
children,
data,
};
Left(Ok(()))
}
}
}
}
#[inline(never)]
fn insert_into_prefix(
node: &mut Node,
mut path: &Path,
ty: PathType,
data: DataKey,
) -> Result<usize, TryInsertError> {
let orig_path = path;
let mut next = Some((node, 0));
loop {
let (node, depth) = next.take().unwrap();
if let Some(prefix) = node.common_prefix(path) {
match prefix {
Prefix::Exact => {
node.set_type(ty)?;
node.set_data(data);
break Ok(depth);
}
Prefix::Child => {
path = path.strip_prefix(node.component()).unwrap();
match node.try_insert_new(orig_path, path, ty, Some(data)) {
Left(result) => {
result?;
break Ok(depth + 1);
}
Right(next_child) => {
next = Some((next_child, depth + 1));
continue;
}
}
}
Prefix::Ancestor => {
node.split_at(path.to_path_buf());
node.set_data(data);
break Ok(depth);
}
Prefix::Partial(common_prefix) => {
let component = path.strip_prefix(&common_prefix).unwrap().to_path_buf();
node.split_at(common_prefix);
node.push_child(component, ty, Some(data))?;
break Ok(depth);
}
}
}
}
}
#[derive(Debug)]
struct DfsVisitor<'a> {
worklist: &'a [Node],
prefix: PathBuf,
component_prefix: PathBuf,
queued: Option<(PathBuf, usize, &'a [Node])>,
depth: usize,
only_leaves: bool,
}
impl<'a> DfsVisitor<'a> {
fn new(prefix: PathBuf, worklist: &'a [Node], depth: usize, only_leaves: bool) -> Self {
Self {
worklist,
prefix,
component_prefix: PathBuf::new(),
queued: None,
depth,
only_leaves,
}
}
pub fn next(
&mut self,
max_depth: Option<usize>,
) -> Option<Either<(PathBuf, DataKey), DfsVisitor<'a>>> {
if let Some((prefix, relative_depth, worklist)) = self.queued.take() {
return Some(Right(DfsVisitor::new(
prefix,
worklist,
relative_depth,
self.only_leaves,
)));
}
loop {
match self.worklist.split_first()? {
(Node::Leaf { data: None, .. }, worklist) => {
self.worklist = worklist;
continue;
}
(
Node::Leaf {
component,
data: Some(data),
..
},
worklist,
) => {
self.worklist = worklist;
if self.exceeds_max_depth(component, max_depth) {
continue;
}
let suffix = self.strip_prefix(component);
let prefix = self.prefix.join(suffix);
break Some(Left((prefix, *data)));
}
(
Node::Branch {
component,
children,
data,
},
worklist,
) => {
self.worklist = worklist;
let relative_depth = self.relative_depth(component, max_depth);
if let Some(max_depth) = max_depth
&& relative_depth > max_depth
{
continue;
}
let suffix = self.strip_prefix(component);
let prefix = self.prefix.join(suffix);
if !children.is_empty() {
match data {
Some(data) => {
self.queued =
Some((prefix.clone(), relative_depth, children.as_slice()));
break Some(Left((prefix, *data)));
}
None => {
break Some(Right(DfsVisitor::new(
prefix,
children.as_slice(),
relative_depth,
self.only_leaves,
)));
}
}
}
}
}
}
}
fn exceeds_max_depth(&self, path: &Path, max_depth: Option<usize>) -> bool {
match max_depth {
None => false,
Some(max_depth) => {
let suffix = self.strip_prefix(path);
let relative_depth = self.depth + suffix.components().count();
relative_depth > max_depth
}
}
}
fn relative_depth(&self, path: &Path, max_depth: Option<usize>) -> usize {
match max_depth {
None => 0,
Some(_) => {
let suffix = self.strip_prefix(path);
self.depth + suffix.components().count()
}
}
}
#[inline]
fn strip_prefix<'p>(&self, path: &'p Path) -> &'p Path {
if self.component_prefix == Path::new("") {
return path;
}
path.strip_prefix(&self.component_prefix).unwrap()
}
}
struct Dfs<'a, V> {
data: &'a [V],
stack: Vec<DfsVisitor<'a>>,
current: DfsVisitor<'a>,
max_depth: Option<usize>,
}
impl<V> core::fmt::Debug for Dfs<'_, V> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("Dfs")
.field("stack", &self.stack)
.field("current", &self.current)
.field("max_depth", &self.max_depth)
.finish()
}
}
impl<'a, V> Dfs<'a, V> {
fn new(tree: &'a PathPrefixTree<V>, only_leaves: bool, max_depth: Option<usize>) -> Self {
Self {
data: tree.data.as_slice(),
stack: vec![],
current: DfsVisitor::new(PathBuf::new(), tree.tree.roots.as_slice(), 0, only_leaves),
max_depth,
}
}
fn at(
tree: &'a PathPrefixTree<V>,
path: &Path,
only_leaves: bool,
max_depth: Option<usize>,
) -> Self {
match tree
.tree
.roots
.iter()
.find(|root| root.has_common_prefix(path))
{
None => Self::empty(tree, only_leaves, max_depth),
Some(root) => {
let mut next = Some(root);
let mut input_path = path;
let mut prefix = PathBuf::new();
loop {
let node = next.take().unwrap();
match node.common_prefix(input_path) {
None => break Self::empty(tree, only_leaves, max_depth),
Some(Prefix::Exact) => {
break Self {
data: tree.data.as_slice(),
stack: vec![],
current: DfsVisitor::new(
prefix,
core::slice::from_ref(node),
0,
only_leaves,
),
max_depth,
};
}
Some(Prefix::Ancestor) => {
prefix.push(input_path);
break Self {
data: tree.data.as_slice(),
stack: vec![],
current: DfsVisitor {
component_prefix: PathBuf::from(input_path),
..DfsVisitor::new(
prefix,
core::slice::from_ref(node),
0,
only_leaves,
)
},
max_depth,
};
}
Some(Prefix::Child) => {
match node {
Node::Branch {
component,
children,
..
} => {
input_path = input_path.strip_prefix(component).unwrap();
next =
children.iter().find(|c| c.has_common_prefix(input_path));
if next.is_none() {
break Self::empty(tree, only_leaves, max_depth);
}
prefix.push(component);
}
Node::Leaf { .. } => {
break Self::empty(tree, only_leaves, max_depth);
}
}
}
Some(Prefix::Partial(_)) => {
break Self::empty(tree, only_leaves, max_depth);
}
}
}
}
}
}
#[inline]
fn empty(tree: &'a PathPrefixTree<V>, only_leaves: bool, max_depth: Option<usize>) -> Self {
Self {
data: tree.data.as_slice(),
stack: vec![],
current: DfsVisitor::new(PathBuf::new(), &[], 0, only_leaves),
max_depth,
}
}
}
impl<'a, V> Iterator for Dfs<'a, V> {
type Item = Entry<'a, V>;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.current.next(self.max_depth) {
None => {
self.current = self.stack.pop()?;
}
Some(Left((path, key))) => {
return Some(Entry {
path,
data: &self.data[key.as_usize()],
});
}
Some(Right(visitor)) => {
let suspended = core::mem::replace(&mut self.current, visitor);
self.stack.push(suspended);
}
}
}
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn path_tree_insert() {
let tree = PathPrefixTree::<()>::from_iter(["/foo/bar", "/foo/bar/baz", "/qux"]);
let child = Node::Leaf {
component: PathBuf::from("baz"),
ty: PathType::Unknown,
data: Some(DataKey(1)),
};
let a = Node::Branch {
component: PathBuf::from("foo"),
children: vec![Node::Branch {
component: PathBuf::from("bar"),
children: vec![child],
data: Some(DataKey(0)),
}],
data: None,
};
let b = Node::Leaf {
component: PathBuf::from("qux"),
ty: PathType::Unknown,
data: Some(DataKey(2)),
};
let root = Node::Branch {
component: PathBuf::from("/"),
children: vec![a, b],
data: None,
};
assert_eq!(tree.tree.roots.as_slice(), &[root]);
}
#[test]
fn path_tree_nearest_ancestor() {
let tree = PathPrefixTree::<()>::from_iter(["/foo/bar", "/foo/bar/baz", "/qux"]);
assert_eq!(
tree.nearest_ancestor("/foo/bar/baz").map(Entry::into_path),
Some(PathBuf::from("/foo/bar"))
);
assert_eq!(
tree.nearest_ancestor("/foo/bar").map(Entry::into_path),
None
);
assert_eq!(tree.nearest_ancestor("/qux").map(Entry::into_path), None);
}
#[test]
fn path_tree_contains() {
let tree = PathPrefixTree::<()>::from_iter(["/foo/bar", "/foo/bar/baz", "/qux"]);
assert!(tree.contains("/foo/bar/baz"));
assert!(tree.contains("/foo/bar"));
assert!(!tree.contains("/foo"));
assert!(!tree.contains("/foo/bar/baz/thing.txt"));
}
#[test]
fn path_tree_get() {
let mut tree = PathPrefixTree::default();
tree.insert("/foo/bar/baz", 1usize);
tree.insert("/foo/bar/baz/qux", 2usize);
assert_eq!(tree.get("/foo/bar/baz/qux").copied(), Some(2));
assert_eq!(tree.get("/foo/bar/baz").copied(), Some(1));
}
#[test]
fn path_tree_iter() {
let tree = PathPrefixTree::<()>::from_iter(["/qux", "/foo/bar/baz", "/foo/bar"]);
let paths = tree.iter().map(|e| e.into_path()).collect::<Vec<_>>();
let expected = vec![
PathBuf::from("/foo/bar"),
PathBuf::from("/foo/bar/baz"),
PathBuf::from("/qux"),
];
assert_eq!(paths, expected);
}
#[test]
fn path_tree_children() {
let tree = PathPrefixTree::<()>::from_iter(["/qux", "/foo/bar/baz", "/foo/bar"]);
let paths = tree
.children("/foo/bar", None)
.map(|e| e.into_path())
.collect::<Vec<_>>();
let expected = vec![PathBuf::from("/foo/bar"), PathBuf::from("/foo/bar/baz")];
assert_eq!(paths, expected);
let paths = tree
.children("/foo", None)
.map(|e| e.into_path())
.collect::<Vec<_>>();
assert_eq!(paths, expected);
}
}