use std::fmt::{Display, Formatter};
use crate::value::Value;
#[derive(Debug, PartialEq)]
pub struct Undo {
remove: usize,
insert: Vec<Value>,
tokens: Vec<String>,
}
impl Undo {
pub fn new(remove: usize, insert: Vec<Value>, tokens: Vec<String>) -> Undo {
Self { remove, insert, tokens }
}
pub fn is_empty(&self) -> bool {
self.remove == 0 && self.insert.is_empty()
}
pub fn merge(&mut self, remove: usize, mut insert: Vec<Value>, token: Option<&str>) {
if let Some(start) = insert.len().checked_sub(self.remove) {
insert.drain(start..);
insert.append(&mut self.insert);
self.remove = remove;
self.insert = insert;
} else if let Some(delta) = self.remove.checked_sub(insert.len()) {
self.remove = remove + delta;
}
if let Some(token) = token {
self.tokens.push(String::from(token));
}
}
pub fn clear(&mut self) {
self.remove = 0;
self.insert.clear();
}
pub fn apply_to(&mut self, stack: &mut Vec<Value>) -> Undo {
let start = stack.len().checked_sub(self.remove).unwrap_or(0);
let remove = self.insert.len();
let insert = stack.drain(start..).collect();
let tokens = self.tokens.drain(..).collect();
stack.append(&mut self.insert);
self.remove = 0;
return Self::new(remove, insert, tokens);
}
}
impl Default for Undo {
fn default() -> Undo {
Self::new(0, Vec::new(), Vec::new())
}
}
impl Display for Undo {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let tokens = self.tokens.join(" ");
write!(f, "{}", tokens)?;
return Ok(());
}
}
#[cfg(test)]
pub mod tests {
use pretty_assertions::assert_eq;
use crate::engine::tests::create_values;
use crate::undo::Undo;
#[test]
fn test_values_are_merged_with_underflow() {
let mut undo = create_undo(1, vec!["2", "3"], vec![]);
undo.merge(1, create_values(vec!["1", "5"]), None);
assert_eq!(undo.remove, 1);
assert_eq!(undo.insert, create_values(vec!["1", "2", "3"]));
}
#[test]
fn test_values_are_merged_with_overflow() {
let mut undo = create_undo(3, vec!["2", "3"], vec![]);
undo.merge(1, create_values(vec!["1", "5"]), None);
assert_eq!(undo.remove, 2);
assert_eq!(undo.insert, create_values(vec!["2", "3"]));
}
#[test]
fn test_changes_are_applied_with_underflow() {
let mut stack = create_values(vec!["1", "2", "3"]);
let mut undo = create_undo(2, vec!["4", "5"], vec![]);
undo.apply_to(&mut stack);
assert_eq!(stack, create_values(vec!["1", "4", "5"]));
}
#[test]
fn test_changes_are_applied_with_overflow() {
let mut stack = create_values(vec!["1", "2", "3"]);
let mut undo = create_undo(4, vec!["4", "5"], vec![]);
undo.apply_to(&mut stack);
assert_eq!(stack, create_values(vec!["4", "5"]));
}
pub fn create_undo(remove: usize, insert: Vec<&str>, tokens: Vec<&str>) -> Undo {
let insert = create_values(insert);
let tokens = tokens.iter().map(|x| x.to_string()).collect();
return Undo::new(remove, insert, tokens);
}
}