Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion src/empty_zipper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,11 @@ impl ZipperIteration for EmptyZipper {
fn to_next_val_observed<Obs: PathObserver>(&mut self, _obs: &mut Obs) -> bool { false }
fn descend_last_path_observed<Obs: PathObserver>(&mut self, _obs: &mut Obs) -> bool { false }
fn descend_first_k_path_observed<Obs: PathObserver>(&mut self, _k: usize, _obs: &mut Obs) -> bool { false }
fn to_next_k_path_observed<Obs: PathObserver>(&mut self, _k: usize, _obs: &mut Obs) -> bool { false }
fn to_next_k_path_observed<Obs: PathObserver>(&mut self, k: usize, obs: &mut Obs) -> bool {
let ascended = self.ascend(k);
obs.ascend(ascended);
false
}
}

impl<V> ZipperValues<V> for EmptyZipper {
Expand Down Expand Up @@ -136,3 +140,24 @@ impl ZipperPathBuffer for EmptyZipper {
crate::impl_name_only_debug!(
impl core::fmt::Debug for EmptyZipper
);

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn to_next_k_path_rewinds_and_reports_movement() {
let mut z = EmptyZipper::new_at_path(b"root.");
z.descend_to(b"abc");
let mut observed = z.path().to_vec();

assert!(!z.to_next_k_path_observed(2, &mut observed));
assert_eq!(z.path(), b"a");
assert_eq!(observed, z.path());
assert_eq!(z.root_prefix_path(), b"root.");

assert!(!z.to_next_k_path_observed(5, &mut observed));
assert_eq!(z.path(), b"");
assert_eq!(observed, z.path());
}
}
28 changes: 27 additions & 1 deletion src/prefix_zipper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,15 @@ impl<'prefix, Z> ZipperIteration for PrefixZipper<'prefix, Z>
}

fn to_next_k_path_observed<Obs: PathObserver>(&mut self, k: usize, obs: &mut Obs) -> bool {
if self.position.is_invalid() || self.depth() < k {
let depth = self.depth();
if depth < k {
self.reset();
obs.ascend(depth);
return false;
}
if self.position.is_invalid() {
self.ascend(k);
obs.ascend(k);
return false;
}
//Only the portion of `k` inside the source can have alternatives to step to, so `k` is
Expand All @@ -597,6 +605,9 @@ impl<'prefix, Z> ZipperIteration for PrefixZipper<'prefix, Z>
let source_depth = self.source.depth();
if source_depth == 0 {
//Entirely within the prefix, which offers no alternatives
let ascended = self.ascend(k);
debug_assert_eq!(ascended, k);
obs.ascend(k);
return false
}
let clamped = k.min(source_depth);
Expand Down Expand Up @@ -834,6 +845,7 @@ mod tests {
assert_eq!(z.path(), b"fix.b");
assert_eq!(&obs[..], z.path());
assert!(!z.to_next_k_path_observed(5, &mut obs));
assert_eq!(z.path(), b"");
assert_eq!(&obs[..], z.path());

//k_path where `k` lands inside the prefix, which is a single forced path with no siblings
Expand All @@ -843,6 +855,20 @@ mod tests {
assert_eq!(z.path(), b"fi");
assert_eq!(&obs[..], z.path());
assert!(!z.to_next_k_path_observed(2, &mut obs));
assert_eq!(z.path(), b"");
assert_eq!(&obs[..], z.path());

//A missing path within the prefix also rewinds to the common root.
let mut z = make();
z.descend_to(b"fix.x");
let mut obs = z.path().to_vec();
assert!(!z.to_next_k_path_observed(2, &mut obs));
assert_eq!(z.path(), b"fix");
assert_eq!(&obs[..], z.path());

//An oversized step resets the zipper to its root, including the observer.
assert!(!z.to_next_k_path_observed(99, &mut obs));
assert_eq!(z.path(), b"");
assert_eq!(&obs[..], z.path());

//`k` deeper than anything the source can supply, so the prefix bytes descended on the way
Expand Down