blob: 640e13f7117aa4b68a7328ddaa18b98ffe055f61 [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::NamespaceError;
use cm_types::{IterablePath, Name, NamespacePath};
use std::collections::BTreeMap;
/// A tree representation of a namespace.
///
/// Each leaf node may hold an arbitrary payload `T`. Usually this is a directory client endpoint
/// or client proxy.
#[derive(Debug)]
pub struct Tree<T> {
root: Node<T>,
}
impl<T> Tree<T> {
pub fn new() -> Self {
Tree { root: Node::Internal(BTreeMap::new()) }
}
pub fn add(self: &mut Self, path: &NamespacePath, thing: T) -> Result<&mut T, NamespaceError> {
let names = path.split();
self.root.add(names.into_iter(), thing).map_err(|e| match e {
AddError::Shadow => NamespaceError::Shadow(path.clone()),
AddError::Duplicate => NamespaceError::Duplicate(path.clone()),
})
}
pub fn get_mut(&mut self, path: &NamespacePath) -> Option<&mut T> {
self.root.get_mut(path.iter_segments())
}
pub fn get(&self, path: &NamespacePath) -> Option<&T> {
self.root.get(path.iter_segments())
}
pub fn remove(&mut self, path: &NamespacePath) -> Option<T> {
self.root.remove(path.iter_segments().peekable())
}
pub fn flatten(self) -> Vec<(NamespacePath, T)> {
self.root
.flatten()
.into_iter()
.map(|(path, thing)| (NamespacePath::new(path).unwrap(), thing))
.collect()
}
pub fn map_ref<R>(&self, mut f: impl FnMut(&T) -> R) -> Tree<R> {
Tree { root: self.root.map_ref(&mut f) }
}
}
impl<T> Default for Tree<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Clone> Clone for Tree<T> {
fn clone(&self) -> Self {
Self { root: self.root.map_ref(&mut <T as Clone>::clone) }
}
}
#[derive(Debug)]
enum Node<T> {
Internal(BTreeMap<Name, Node<T>>),
Leaf(T),
}
enum AddError {
Shadow,
Duplicate,
}
impl<T> Node<T> {
fn add(&mut self, mut path: std::vec::IntoIter<Name>, thing: T) -> Result<&mut T, AddError> {
match path.next() {
Some(name) => match self {
Node::Leaf(_) => Err(AddError::Shadow),
Node::Internal(children) => {
let entry =
children.entry(name).or_insert_with(|| Node::Internal(BTreeMap::new()));
entry.add(path, thing)
}
},
None => {
match self {
Node::Internal(children) => {
if !children.is_empty() {
return Err(AddError::Shadow);
}
}
Node::Leaf(_) => {
return Err(AddError::Duplicate);
}
}
*self = Node::Leaf(thing);
match self {
Node::Internal(_) => unreachable!(),
Node::Leaf(ref mut t) => Ok(t),
}
}
}
}
fn get_mut<'a, 'b, I>(&'a mut self, mut path: I) -> Option<&'a mut T>
where
I: Iterator<Item = &'b Name>,
{
match path.next() {
Some(name) => match self {
Node::Leaf(_) => None,
Node::Internal(children) => match children.get_mut(name) {
Some(node) => node.get_mut(path),
None => None,
},
},
None => match self {
Node::Internal(_) => None,
Node::Leaf(ref mut n) => Some(n),
},
}
}
fn get<'a, 'b, I>(&'a self, mut path: I) -> Option<&'a T>
where
I: Iterator<Item = &'b Name>,
{
match path.next() {
Some(name) => match self {
Node::Leaf(_) => None,
Node::Internal(children) => match children.get(name) {
Some(node) => node.get(path),
None => None,
},
},
None => match self {
Node::Internal(_) => None,
Node::Leaf(ref n) => Some(n),
},
}
}
fn remove<'a, I>(&mut self, mut path: std::iter::Peekable<I>) -> Option<T>
where
I: Iterator<Item = &'a Name>,
{
match path.next() {
Some(name) => match self {
Node::Leaf(_) => None,
Node::Internal(children) => {
if path.peek().is_none() {
match children.remove(name) {
Some(Node::Leaf(n)) => Some(n),
Some(Node::Internal(c)) => {
children.insert(name.clone(), Node::Internal(c));
return None;
}
None => None,
}
} else {
match children.get_mut(name) {
Some(node) => node.remove(path),
None => None,
}
}
}
},
None => None,
}
}
pub fn flatten(self) -> Vec<(String, T)> {
fn recurse<T>(path: &str, node: Node<T>, result: &mut Vec<(String, T)>) {
match node {
Node::Internal(map) => {
for (key, value) in map {
let path = format!("{}/{}", path, key);
recurse(&path, value, result);
}
}
Node::Leaf(leaf) => {
// The single empty slash is a special case.
// When there are intermediate nodes, we can append "/{path}" to the previous
// path segment. But if the root is a leaf node, there will be no slash unless
// we add one here.
let path = if path.is_empty() { "/" } else { path };
result.push((path.to_string(), leaf));
}
}
}
let mut result = Vec::new();
recurse("", self, &mut result);
result
}
pub fn map_ref<R>(&self, f: &mut impl FnMut(&T) -> R) -> Node<R> {
match self {
Node::Internal(map) => Node::Internal(BTreeMap::from_iter(
map.iter().map(|(k, v)| (k.clone(), v.map_ref(f))),
)),
Node::Leaf(t) => Node::Leaf(f(t)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
fn ns_path(str: &str) -> NamespacePath {
str.parse().unwrap()
}
#[test]
fn test_add() {
let mut tree = Tree::new();
tree.add(&ns_path("/foo"), 1).unwrap();
tree.add(&ns_path("/bar"), 2).unwrap();
tree.add(&ns_path("/x/y/z"), 3).unwrap();
tree.add(&ns_path("/x/x/z"), 4).unwrap();
tree.add(&ns_path("/x/y/w"), 5).unwrap();
assert_matches!(tree.add(&ns_path("/foo"), 6), Err(NamespaceError::Duplicate(_)));
assert_matches!(tree.add(&ns_path("/bar"), 7), Err(NamespaceError::Duplicate(_)));
tree.add(&ns_path("/a/b/c"), 0).unwrap();
assert_matches!(tree.add(&ns_path("/"), 0), Err(NamespaceError::Shadow(_)));
assert_matches!(tree.add(&ns_path("/a"), 0), Err(NamespaceError::Shadow(_)));
assert_matches!(tree.add(&ns_path("/a/b"), 0), Err(NamespaceError::Shadow(_)));
assert_matches!(tree.add(&ns_path("/a/b/c"), 0), Err(NamespaceError::Duplicate(_)));
assert_matches!(tree.add(&ns_path("/a/b/c/d"), 0), Err(NamespaceError::Shadow(_)));
assert_matches!(tree.add(&ns_path("/a/b/c/d/e"), 0), Err(NamespaceError::Shadow(_)));
}
#[test]
fn test_root() {
let mut tree = Tree::new();
tree.add(&ns_path("/"), 1).unwrap();
assert_matches!(
tree.add(&ns_path("/"), 2),
Err(NamespaceError::Duplicate(path)) if path.to_string() == "/"
);
assert_matches!(
tree.add(&ns_path("/a"), 3),
Err(NamespaceError::Shadow(path)) if path.to_string() == "/a"
);
}
#[test]
fn test_get_mut() {
let mut tree = Tree::new();
tree.add(&ns_path("/foo"), 1).unwrap();
assert_eq!(*tree.get_mut(&ns_path("/foo")).unwrap(), 1);
*tree.get_mut(&ns_path("/foo")).unwrap() = 2;
assert_eq!(*tree.get_mut(&ns_path("/foo")).unwrap(), 2);
assert_matches!(tree.get_mut(&ns_path("/bar")), None);
tree.add(&ns_path("/a/b"), 1).unwrap();
assert_matches!(tree.get_mut(&ns_path("/a")), None);
assert_matches!(tree.get_mut(&ns_path("/a/b/c")), None);
assert_eq!(*tree.get_mut(&ns_path("/a/b")).unwrap(), 1);
*tree.get_mut(&ns_path("/a/b")).unwrap() = 2;
assert_eq!(*tree.get_mut(&ns_path("/a/b")).unwrap(), 2);
}
#[test]
fn test_get() {
let mut tree = Tree::new();
assert_eq!(tree.get(&ns_path("/foo")), None);
tree.add(&ns_path("/foo"), 1).unwrap();
assert_eq!(*tree.get(&ns_path("/foo")).unwrap(), 1);
tree.add(&ns_path("/a/b"), 1).unwrap();
assert_matches!(tree.get_mut(&ns_path("/a")), None);
assert_eq!(*tree.get(&ns_path("/a/b")).unwrap(), 1);
assert_matches!(tree.get_mut(&ns_path("/a/b/c")), None);
}
#[test]
fn test_remove() {
let mut tree = Tree::new();
assert_matches!(tree.remove(&ns_path("/foo")), None);
tree.add(&ns_path("/foo"), 1).unwrap();
assert_matches!(tree.remove(&ns_path("/foo")), Some(1));
assert_matches!(tree.remove(&ns_path("/foo")), None);
tree.add(&ns_path("/foo/bar"), 1).unwrap();
assert_matches!(tree.remove(&ns_path("/foo")), None);
assert_matches!(tree.remove(&ns_path("/foo/bar/baz")), None);
assert_matches!(tree.remove(&ns_path("/foo/bar")), Some(1));
assert_matches!(tree.remove(&ns_path("/foo/bar")), None);
}
#[test]
fn test_flatten() {
let mut tree = Tree::new();
tree.add(&ns_path("/a/b/c"), 1).unwrap();
tree.add(&ns_path("/b/c/d/e"), 2).unwrap();
tree.add(&ns_path("/b/c/e/f"), 3).unwrap();
let mut entries = tree.flatten();
entries.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
assert_eq!(entries.len(), 3);
assert_eq!(entries[0].0.to_string(), "/a/b/c");
assert_eq!(entries[0].1, 1);
assert_eq!(entries[1].0.to_string(), "/b/c/d/e");
assert_eq!(entries[1].1, 2);
assert_eq!(entries[2].0.to_string(), "/b/c/e/f");
assert_eq!(entries[2].1, 3);
}
#[test]
fn test_flatten_root() {
let mut tree = Tree::new();
tree.add(&ns_path("/"), 1).unwrap();
let entries = tree.flatten();
assert_eq!(entries.len(), 1);
assert_eq!(entries[0].0.to_string(), "/");
assert_eq!(entries[0].1, 1);
}
#[test]
fn test_clone() {
let mut tree = Tree::new();
tree.add(&ns_path("/foo"), 1).unwrap();
tree.add(&ns_path("/bar/baz"), 2).unwrap();
let tree_clone = tree.clone();
let mut entries = tree.flatten();
entries.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
let mut entries_clone = tree_clone.flatten();
entries_clone.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
assert_eq!(entries, entries_clone);
}
}