use super::{EditorState, level_here};
use crate::sim::Board;
use std::collections::VecDeque;
pub(super) const DEPTH: usize = 100;
const SNAPSHOT_NAME: &str = "-";
pub(super) fn snapshot(state: &EditorState, board: &Board) -> String {
level_here(state, board, SNAPSHOT_NAME).to_text()
}
#[derive(Default, Debug)]
pub(super) struct History {
now: Option<String>,
back: VecDeque<String>,
ahead: Vec<String>,
}
impl History {
pub(super) fn starting_at(now: String) -> Self {
History {
now: Some(now),
..History::default()
}
}
pub(super) fn is_now(&self, text: &str) -> bool {
self.now.as_deref() == Some(text)
}
pub(super) fn amend(&mut self, now: String) {
if self.now.is_some() {
self.now = Some(now);
}
}
pub(super) fn record(&mut self, after: String, merge: bool) {
if self.is_now(&after) {
return;
}
if let Some(before) = self.now.replace(after)
&& !merge
{
if self.back.len() == DEPTH {
self.back.pop_front();
}
self.back.push_back(before);
}
self.ahead.clear();
}
pub(super) fn undo(&mut self) -> Option<String> {
let before = self.back.pop_back()?;
if let Some(now) = self.now.replace(before.clone()) {
self.ahead.push(now);
}
Some(before)
}
pub(super) fn redo(&mut self) -> Option<String> {
let next = self.ahead.pop()?;
if let Some(now) = self.now.replace(next.clone()) {
self.back.push_back(now);
}
Some(next)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn text(n: usize) -> String {
format!("level {n}")
}
#[test]
fn undo_and_redo_walk_the_edits() {
let mut history = History::starting_at(text(0));
assert_eq!(history.undo(), None, "nothing before the level came in");
history.record(text(1), false);
history.record(text(2), false);
assert_eq!(history.undo(), Some(text(1)));
assert_eq!(history.undo(), Some(text(0)));
assert_eq!(history.undo(), None);
assert_eq!(history.redo(), Some(text(1)));
history.record(text(3), false);
assert_eq!(history.redo(), None, "a new edit drops what was undone");
assert_eq!(history.undo(), Some(text(1)));
}
#[test]
fn strokes_merge_and_the_depth_holds() {
let mut history = History::starting_at(text(0));
history.record(text(1), false);
history.record(text(2), true);
history.record(text(2), false);
assert_eq!(history.undo(), Some(text(0)), "the stroke went whole");
let mut full = History::starting_at(text(0));
for n in 1..=DEPTH + 5 {
full.record(text(n), false);
}
let mut steps = 0;
while full.undo().is_some() {
steps += 1;
}
assert_eq!(steps, DEPTH);
}
}