use ahash::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct PathIndex(usize);
impl PathIndex {
#[inline]
fn new(index: usize) -> Self {
Self(index)
}
#[inline]
fn get(self) -> usize {
self.0
}
#[inline]
pub(crate) fn root() -> Self {
Self(0)
}
}
#[derive(Debug, Default)]
struct PathNode<'p> {
children: HashMap<&'p str, PathIndex>,
marked: bool,
}
#[derive(Debug)]
pub(crate) struct Trie<'p> {
nodes: Vec<PathNode<'p>>,
}
impl<'p> Trie<'p> {
fn with_capacity(node_count: usize) -> Self {
let mut nodes = Vec::with_capacity(node_count + 1);
nodes.push(PathNode::default()); Self { nodes }
}
pub(crate) fn from_paths(paths: &[Vec<&'p str>]) -> Self {
let segment_count: usize = paths.iter().map(|path| path.len()).sum();
let mut trie = Self::with_capacity(segment_count);
for path in paths {
trie.add_path(path.iter().copied());
}
trie
}
pub(crate) fn add_path(&mut self, segments: impl Iterator<Item = &'p str>) {
let mut current = PathIndex::root();
let mut peekable = segments.peekable();
while let Some(segment) = peekable.next() {
let is_last = peekable.peek().is_none();
let child = self.nodes[current.get()].children.get(segment).copied();
let next = match child {
Some(child_idx) => child_idx,
None => {
let new_idx = PathIndex::new(self.nodes.len());
self.nodes.push(PathNode::default());
self.nodes[current.get()].children.insert(segment, new_idx);
new_idx
}
};
current = next;
if is_last {
self.nodes[current.get()].marked = true;
}
}
}
#[inline]
pub(super) fn find_segment_at_position(
&self,
parent_path_position: PathIndex,
segment: &str,
) -> Option<(PathIndex, bool)> {
let parent_node = &self.nodes[parent_path_position.get()];
let child_path_position = parent_node.children.get(segment).copied()?;
let child_node = &self.nodes[child_path_position.get()];
Some((child_path_position, child_node.marked))
}
#[inline]
pub(super) fn has_children(&self, path_position: PathIndex) -> bool {
!self.nodes[path_position.get()].children.is_empty()
}
}