use swh_graph::labels::LabelNameId;
const PATH_SEPARATOR: LabelNameId = LabelNameId(u64::MAX);
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct PathStack(Vec<LabelNameId>);
impl PathStack {
pub fn new() -> PathStack {
PathStack(Vec::new())
}
pub fn with_capacity(capacity: usize) -> PathStack {
PathStack(Vec::with_capacity(capacity))
}
#[inline]
pub fn push<Iter: IntoIterator<Item = LabelNameId>>(&mut self, path: Iter)
where
<Iter as IntoIterator>::IntoIter: DoubleEndedIterator,
{
self.0.push(PATH_SEPARATOR);
for item in path.into_iter().rev() {
assert_ne!(
item, PATH_SEPARATOR,
"u64::MAX may not be used as path part"
);
self.0.push(item);
}
}
#[inline]
pub fn push_filename(&mut self, item: LabelNameId) {
assert_ne!(
item, PATH_SEPARATOR,
"u64::MAX may not be used as path part"
);
self.0.push(item);
}
#[inline]
pub fn pop(&mut self) -> Option<PopPathStack<'_>> {
if self.0.is_empty() {
None
} else {
Some(PopPathStack(self))
}
}
}
pub struct PopPathStack<'a>(&'a mut PathStack);
impl Iterator for PopPathStack<'_> {
type Item = LabelNameId;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if *self
.0
.0
.last()
.expect("PopPathStack reached the bottom of the stack")
== PATH_SEPARATOR
{
None
} else {
Some(self.0.0.pop().unwrap())
}
}
}
impl Drop for PopPathStack<'_> {
fn drop(&mut self) {
while self
.0
.0
.pop()
.expect("PopPathStack reached the bottom of the stack")
!= PATH_SEPARATOR
{}
}
}
#[test]
fn test_path_stack_empty() {
let mut stack = PathStack::new();
assert!(stack.pop().is_none());
assert!(stack.pop().is_none());
}
#[test]
fn test_path_stack_drop() {
let path1 = [LabelNameId(0), LabelNameId(10)];
let path2 = [LabelNameId(1)];
let mut stack = PathStack::new();
stack.push(path1);
stack.push(path2);
stack.pop().unwrap(); assert_eq!(stack.pop().unwrap().collect::<Vec<_>>(), path1);
assert!(stack.pop().is_none());
}