const LIMIT: usize = 100;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Trail<T> {
back: Vec<T>,
forward: Vec<T>,
current: Option<T>,
}
impl<T> Default for Trail<T> {
fn default() -> Self {
Self {
back: Vec::new(),
forward: Vec::new(),
current: None,
}
}
}
impl<T: Clone + PartialEq> Trail<T> {
pub fn visit(&mut self, place: T) {
if self.current.as_ref() == Some(&place) {
return;
}
if let Some(left) = self.current.replace(place) {
self.back.push(left);
if self.back.len() > LIMIT {
self.back.remove(0);
}
}
self.forward.clear();
}
pub fn go_back(&mut self) -> Option<T> {
let place = self.back.pop()?;
if let Some(left) = self.current.replace(place.clone()) {
self.forward.push(left);
}
Some(place)
}
pub fn go_forward(&mut self) -> Option<T> {
let place = self.forward.pop()?;
if let Some(left) = self.current.replace(place.clone()) {
self.back.push(left);
}
Some(place)
}
#[must_use]
pub const fn can_go_back(&self) -> bool {
!self.back.is_empty()
}
#[must_use]
pub const fn can_go_forward(&self) -> bool {
!self.forward.is_empty()
}
#[must_use]
pub const fn current(&self) -> Option<&T> {
self.current.as_ref()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_fresh_trail_goes_nowhere() {
let mut trail: Trail<u32> = Trail::default();
assert!(!trail.can_go_back() && !trail.can_go_forward());
assert_eq!(trail.go_back(), None);
assert_eq!(trail.go_forward(), None);
assert_eq!(trail.current(), None);
}
#[test]
fn back_and_forward_walk_the_visits() {
let mut trail = Trail::default();
for place in [1, 2, 3] {
trail.visit(place);
}
assert_eq!(trail.current(), Some(&3));
assert_eq!(trail.go_back(), Some(2));
assert_eq!(trail.go_back(), Some(1));
assert!(!trail.can_go_back() && trail.can_go_forward());
assert_eq!(trail.go_forward(), Some(2));
assert_eq!(trail.current(), Some(&2));
}
#[test]
fn a_new_visit_drops_the_forward_trail() {
let mut trail = Trail::default();
for place in [1, 2, 3] {
trail.visit(place);
}
trail.go_back();
trail.visit(9);
assert!(!trail.can_go_forward());
assert_eq!(trail.go_back(), Some(2));
assert_eq!(trail.go_back(), Some(1));
}
#[test]
fn visiting_the_place_already_shown_changes_nothing() {
let mut trail = Trail::default();
trail.visit(1);
trail.visit(1);
assert!(!trail.can_go_back());
trail.visit(2);
trail.visit(2);
assert_eq!(trail.go_back(), Some(1));
assert_eq!(trail.go_back(), None);
}
#[test]
fn the_trail_keeps_only_the_latest_places() {
let mut trail = Trail::default();
for place in 0..(LIMIT as u32 + 20) {
trail.visit(place);
}
let mut steps = 0;
while trail.go_back().is_some() {
steps += 1;
}
assert_eq!(steps, LIMIT);
}
}