#![no_std]
#![forbid(unsafe_code)]
#![warn(clippy::alloc_instead_of_core, clippy::std_instead_of_alloc)]
#![warn(clippy::pedantic, clippy::cargo)]
#![allow(clippy::module_name_repetitions)]
#![warn(missing_docs, clippy::missing_docs_in_private_items)]
extern crate alloc;
use core::{cmp, fmt, hash, ops};
use alloc::vec::Vec;
#[derive(Clone, Default, Debug)]
pub struct HistoryStack<T> {
stack: Vec<T>,
current: T,
}
impl<T: fmt::Display> fmt::Display for HistoryStack<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.current.fmt(f)
}
}
impl<T> HistoryStack<T> {
pub const fn new(v: T) -> Self {
Self {
stack: Vec::new(),
current: v,
}
}
pub fn pop(&mut self) -> Option<T> {
match self.stack.pop() {
Some(last) => Some(core::mem::replace(&mut self.current, last)),
None => None,
}
}
pub fn push_value(&mut self, v: T) {
self.stack.push(core::mem::replace(&mut self.current, v));
}
pub fn push(&mut self)
where
T: Clone,
{
self.stack.push(self.current.clone());
}
}
impl<T> ops::Deref for HistoryStack<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.current
}
}
impl<T> ops::DerefMut for HistoryStack<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.current
}
}
impl<T: PartialEq> PartialEq<T> for HistoryStack<T> {
fn eq(&self, other: &T) -> bool {
&self.current == other
}
}
impl<T: PartialEq> PartialEq for HistoryStack<T> {
fn eq(&self, other: &Self) -> bool {
self.current == other.current
}
}
impl<T: Eq> Eq for HistoryStack<T> {}
impl<T: PartialOrd> PartialOrd for HistoryStack<T> {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
self.current.partial_cmp(&other.current)
}
}
impl<T: PartialOrd> PartialOrd<T> for HistoryStack<T> {
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
self.current.partial_cmp(other)
}
}
impl<T: Ord> Ord for HistoryStack<T> {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.current.cmp(&other.current)
}
}
impl<T: hash::Hash> hash::Hash for HistoryStack<T> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.current.hash(state);
}
}
#[derive(Clone, Debug)]
pub struct UndoStack<T> {
history: Vec<T>,
current: usize,
}
impl<T: Default> Default for UndoStack<T> {
fn default() -> Self {
Self {
history: alloc::vec![T::default()],
current: 0,
}
}
}
impl<T: fmt::Display> fmt::Display for UndoStack<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.inner().fmt(f)
}
}
impl<T> UndoStack<T> {
pub fn new(start: T) -> Self {
Self {
history: alloc::vec![start],
current: 0,
}
}
fn invalidate_future(&mut self) {
if self.current + 1 != self.history.len() {
self.history.truncate(self.current + 1);
}
}
fn push_unchecked(&mut self, val: T) -> &mut T {
self.history.push(val);
self.current += 1;
&mut self.history[self.current]
}
pub fn save(&mut self) -> &mut T
where
T: Clone,
{
self.invariant_ck();
self.invalidate_future();
let val = self.history.last().unwrap().clone();
self.push_unchecked(val)
}
pub fn push(&mut self, new_current: T) -> &mut T {
self.invariant_ck();
self.invalidate_future();
self.push_unchecked(new_current)
}
#[allow(clippy::missing_errors_doc)]
pub fn undo(&mut self) -> Result<&mut T, &mut T> {
self.invariant_ck();
match self.current.checked_sub(1) {
Some(n) => {
self.current = n;
Ok(&mut self.history[self.current])
}
None => {
Err(&mut self.history[0])
}
}
}
#[allow(clippy::missing_errors_doc)]
pub fn redo(&mut self) -> Result<&mut T, &mut T> {
self.invariant_ck();
if self.current + 1 == self.history.len() {
Err(&mut self.history[self.current])
} else {
self.current += 1;
Ok(&mut self.history[self.current])
}
}
fn invariant_ck(&self) {
debug_assert!(
!self.history.is_empty(),
"UndoStack: history was empty, this indicates a bug in UndoStack"
);
debug_assert!(self.current < self.history.len(), "UndoStack: current was not less than history length, this indicates a bug in UndoStack");
}
fn inner(&self) -> &T {
self
}
}
impl<T> ops::Deref for UndoStack<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.history[self.current]
}
}
impl<T> ops::DerefMut for UndoStack<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.history[self.current]
}
}
impl<T: PartialEq> PartialEq<T> for UndoStack<T> {
fn eq(&self, other: &T) -> bool {
self.inner() == other
}
}
impl<T: PartialEq> PartialEq for UndoStack<T> {
fn eq(&self, other: &Self) -> bool {
self.inner() == other.inner()
}
}
impl<T: Eq> Eq for UndoStack<T> {}
impl<T: PartialOrd> PartialOrd for UndoStack<T> {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
self.inner().partial_cmp(other.inner())
}
}
impl<T: PartialOrd> PartialOrd<T> for UndoStack<T> {
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
self.inner().partial_cmp(other)
}
}
impl<T: Ord> Ord for UndoStack<T> {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.inner().cmp(other.inner())
}
}
impl<T: hash::Hash> hash::Hash for UndoStack<T> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.inner().hash(state);
}
}
#[test]
fn undo_stack() {
let mut g = UndoStack::new(0u8);
*g.save() += 1;
assert_eq!(g, 1);
assert_eq!(*g.undo().unwrap(), 0);
assert_eq!(*g.redo().unwrap(), 1);
assert!(g.undo().is_ok());
*g.save() += 2;
assert!(g.redo().is_err());
}
#[test]
fn history_stack() {
let mut g = HistoryStack::new(0u8);
g.push_value(5);
assert_eq!(g, 5);
assert_eq!(g.pop(), Some(5));
assert_eq!(g, 0);
}