use std::path::PathBuf;
const MAX_HISTORY: usize = 256;
#[derive(Debug, Clone, Default)]
pub struct NavigationHistory {
back: Vec<PathBuf>,
forward: Vec<PathBuf>,
jumps: Vec<PathBuf>,
}
impl NavigationHistory {
pub fn new() -> Self {
Self::default()
}
pub fn push(&mut self, current_cwd: PathBuf) {
self.forward.clear();
if self.back.len() == MAX_HISTORY {
self.back.remove(0);
}
self.back.push(current_cwd);
}
pub fn back(&mut self, current_cwd: PathBuf) -> Option<PathBuf> {
let prev = self.back.pop()?;
if self.forward.len() == MAX_HISTORY {
self.forward.remove(0);
}
self.forward.push(current_cwd);
Some(prev)
}
pub fn forward(&mut self, current_cwd: PathBuf) -> Option<PathBuf> {
let next = self.forward.pop()?;
if self.back.len() == MAX_HISTORY {
self.back.remove(0);
}
self.back.push(current_cwd);
Some(next)
}
pub fn push_jump(&mut self, from: PathBuf) {
if self.jumps.len() == MAX_HISTORY {
self.jumps.remove(0);
}
self.jumps.push(from);
}
pub fn pop_jump(&mut self) -> Option<PathBuf> {
self.jumps.pop()
}
pub fn can_go_back(&self) -> bool {
!self.back.is_empty()
}
pub fn can_go_forward(&self) -> bool {
!self.forward.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn p(s: &str) -> PathBuf {
PathBuf::from(s)
}
#[test]
fn push_and_back() {
let mut h = NavigationHistory::new();
h.push(p("/a"));
let prev = h.back(p("/b"));
assert_eq!(prev, Some(p("/a")));
assert!(!h.can_go_back());
assert!(h.can_go_forward());
}
#[test]
fn back_then_forward() {
let mut h = NavigationHistory::new();
h.push(p("/a"));
h.push(p("/b"));
let prev = h.back(p("/c"));
assert_eq!(prev, Some(p("/b")));
let next = h.forward(p("/b"));
assert_eq!(next, Some(p("/c")));
}
#[test]
fn push_clears_forward() {
let mut h = NavigationHistory::new();
h.push(p("/a"));
h.back(p("/b"));
h.push(p("/c"));
assert!(!h.can_go_forward());
}
#[test]
fn jumps_are_kept_apart_from_ordinary_history() {
let mut h = NavigationHistory::new();
h.push_jump(p("/start"));
h.push(p("/landed"));
h.push(p("/deeper"));
assert_eq!(h.pop_jump(), Some(p("/start")));
assert_eq!(h.pop_jump(), None);
}
#[test]
fn back_on_empty_returns_none() {
let mut h = NavigationHistory::new();
assert_eq!(h.back(p("/x")), None);
}
#[test]
fn forward_on_empty_returns_none() {
let mut h = NavigationHistory::new();
assert_eq!(h.forward(p("/x")), None);
}
}