use crate::{Checkpoint, Command, Display, History, Merge, Meta, Queue, Signal};
#[cfg(feature = "chrono")]
use chrono::{DateTime, TimeZone, Utc};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "chrono")]
use std::cmp::Ordering;
use std::collections::VecDeque;
use std::fmt;
use std::marker::PhantomData;
use std::num::NonZeroUsize;
#[allow(unsafe_code)]
const MAX_LIMIT: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(usize::max_value()) };
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Record<R, C: Command<R>> {
pub(crate) commands: VecDeque<Meta<C>>,
receiver: R,
current: usize,
limit: NonZeroUsize,
pub(crate) saved: Option<usize>,
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) signal: Option<Box<dyn FnMut(Signal) + Send + Sync + 'static>>,
}
impl<R, C: Command<R>> Record<R, C> {
#[inline]
pub fn new(receiver: impl Into<R>) -> Record<R, C> {
Record {
commands: VecDeque::new(),
receiver: receiver.into(),
current: 0,
limit: MAX_LIMIT,
saved: Some(0),
signal: None,
}
}
#[inline]
pub fn builder() -> RecordBuilder<R, C> {
RecordBuilder {
commands: PhantomData,
receiver: PhantomData,
capacity: 0,
limit: MAX_LIMIT,
saved: true,
signal: None,
}
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.commands.reserve(additional);
}
#[inline]
pub fn capacity(&self) -> usize {
self.commands.capacity()
}
#[inline]
pub fn len(&self) -> usize {
self.commands.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.commands.is_empty()
}
#[inline]
pub fn limit(&self) -> usize {
self.limit.get()
}
#[inline]
pub fn set_limit(&mut self, limit: usize) -> usize {
self.limit = NonZeroUsize::new(limit).expect("limit can not be `0`");
if limit < self.len() {
let old = self.current();
let could_undo = self.can_undo();
let was_saved = self.is_saved();
let begin = old.min(self.len() - limit);
self.commands = self.commands.split_off(begin);
self.limit = NonZeroUsize::new(self.len()).unwrap();
self.current -= begin;
self.saved = self.saved.and_then(|saved| saved.checked_sub(begin));
let new = self.current();
let can_undo = self.can_undo();
let is_saved = self.is_saved();
if let Some(ref mut f) = self.signal {
if old != new {
f(Signal::Current { old, new });
}
if could_undo != can_undo {
f(Signal::Undo(can_undo));
}
if was_saved != is_saved {
f(Signal::Saved(is_saved));
}
}
}
self.limit()
}
#[inline]
pub fn connect(
&mut self,
f: impl FnMut(Signal) + Send + Sync + 'static,
) -> Option<impl FnMut(Signal) + Send + Sync + 'static> {
self.signal
.replace(Box::new(f))
.map(|mut f| move |signal| f(signal))
}
#[inline]
pub fn can_undo(&self) -> bool {
self.current() > 0
}
#[inline]
pub fn can_redo(&self) -> bool {
self.current() < self.len()
}
#[inline]
pub fn set_saved(&mut self, saved: bool) {
let was_saved = self.is_saved();
if saved {
self.saved = Some(self.current());
if let Some(ref mut f) = self.signal {
if !was_saved {
f(Signal::Saved(true));
}
}
} else {
self.saved = None;
if let Some(ref mut f) = self.signal {
if was_saved {
f(Signal::Saved(false));
}
}
}
}
#[inline]
pub fn is_saved(&self) -> bool {
self.saved.map_or(false, |saved| saved == self.current())
}
#[inline]
pub fn revert(&mut self) -> Option<Result<(), C::Error>> {
self.saved.and_then(|saved| self.go_to(saved))
}
#[inline]
pub fn current(&self) -> usize {
self.current
}
#[inline]
pub fn clear(&mut self) {
let old = self.current();
let could_undo = self.can_undo();
let could_redo = self.can_redo();
self.commands.clear();
self.saved = if self.is_saved() { Some(0) } else { None };
self.current = 0;
if let Some(ref mut f) = self.signal {
if old != 0 {
f(Signal::Current { old, new: 0 });
}
if could_undo {
f(Signal::Undo(false));
}
if could_redo {
f(Signal::Redo(false));
}
}
}
#[inline]
pub fn apply(&mut self, command: C) -> Result<(), C::Error> {
self.__apply(Meta::from(command)).map(|_| ())
}
#[inline]
pub(crate) fn __apply(
&mut self,
mut meta: Meta<C>,
) -> Result<(bool, VecDeque<Meta<C>>), C::Error> {
if meta.is_dead() {
return Ok((false, VecDeque::new()));
}
if let Err(error) = meta.apply(&mut self.receiver) {
return Err(error);
}
let current = self.current();
let could_undo = self.can_undo();
let could_redo = self.can_redo();
let was_saved = self.is_saved();
let v = self.commands.split_off(current);
debug_assert_eq!(current, self.len());
self.saved = self.saved.filter(|&saved| saved <= current);
let merged = match self.commands.back_mut() {
Some(ref mut last) if !was_saved => last.merge(meta),
_ => Merge::No(meta),
};
let merged_or_annulled = match merged {
Merge::Yes => true,
Merge::Annul => {
self.commands.pop_back();
true
}
Merge::No(meta) => {
if self.limit() == self.current() {
self.commands.pop_front();
self.saved = self.saved.and_then(|saved| saved.checked_sub(1));
} else {
self.current += 1;
}
self.commands.push_back(meta);
false
}
};
debug_assert_eq!(self.current(), self.len());
if let Some(ref mut f) = self.signal {
f(Signal::Current {
old: current,
new: self.current,
});
if could_redo {
f(Signal::Redo(false));
}
if !could_undo {
f(Signal::Undo(true));
}
if was_saved {
f(Signal::Saved(false));
}
}
Ok((merged_or_annulled, v))
}
#[inline]
pub fn undo(&mut self) -> Option<Result<(), C::Error>> {
let was_saved = self.is_saved();
let old = self.current();
loop {
if !self.can_undo() {
return None;
} else if self.commands[self.current - 1].is_dead() {
self.current -= 1;
self.commands.remove(self.current).unwrap();
} else {
break;
}
}
if let Err(error) = self.commands[self.current - 1].undo(&mut self.receiver) {
return Some(Err(error));
}
self.current -= 1;
let len = self.len();
let is_saved = self.is_saved();
if let Some(ref mut f) = self.signal {
f(Signal::Current {
old,
new: self.current,
});
if old == len {
f(Signal::Redo(true));
}
if old == 1 {
f(Signal::Undo(false));
}
if was_saved != is_saved {
f(Signal::Saved(is_saved));
}
}
Some(Ok(()))
}
#[inline]
pub fn redo(&mut self) -> Option<Result<(), C::Error>> {
let was_saved = self.is_saved();
let old = self.current();
loop {
if !self.can_redo() {
return None;
} else if self.commands[self.current].is_dead() {
self.commands.remove(self.current).unwrap();
} else {
break;
}
}
if let Err(error) = self.commands[self.current].redo(&mut self.receiver) {
return Some(Err(error));
}
self.current += 1;
let len = self.len();
let is_saved = self.is_saved();
if let Some(ref mut f) = self.signal {
f(Signal::Current {
old,
new: self.current,
});
if old == len - 1 {
f(Signal::Redo(false));
}
if old == 0 {
f(Signal::Undo(true));
}
if was_saved != is_saved {
f(Signal::Saved(is_saved));
}
}
Some(Ok(()))
}
#[inline]
pub fn go_to(&mut self, current: usize) -> Option<Result<(), C::Error>> {
if current > self.len() {
return None;
}
let could_undo = self.can_undo();
let could_redo = self.can_redo();
let was_saved = self.is_saved();
let old = self.current();
let signal = self.signal.take();
while self.current() != current {
let f = if current > self.current() {
Record::redo
} else {
Record::undo
};
if let Err(err) = f(self).unwrap() {
self.signal = signal;
return Some(Err(err));
}
}
self.signal = signal;
let can_undo = self.can_undo();
let can_redo = self.can_redo();
let is_saved = self.is_saved();
if let Some(ref mut f) = self.signal {
if old != self.current {
f(Signal::Current {
old,
new: self.current,
});
}
if could_undo != can_undo {
f(Signal::Undo(can_undo));
}
if could_redo != can_redo {
f(Signal::Redo(can_redo));
}
if was_saved != is_saved {
f(Signal::Saved(is_saved));
}
}
Some(Ok(()))
}
#[inline]
#[cfg(feature = "chrono")]
pub fn time_travel<Tz: TimeZone>(&mut self, to: &DateTime<Tz>) -> Option<Result<(), C::Error>> {
let to = Utc.from_utc_datetime(&to.naive_utc());
let current = match self.commands.as_slices() {
([], []) => return None,
(start, []) => match start.binary_search_by(|meta| meta.timestamp.cmp(&to)) {
Ok(current) | Err(current) => current,
},
([], end) => match end.binary_search_by(|meta| meta.timestamp.cmp(&to)) {
Ok(current) | Err(current) => current,
},
(start, end) => match start.last().unwrap().timestamp.cmp(&to) {
Ordering::Less => match start.binary_search_by(|meta| meta.timestamp.cmp(&to)) {
Ok(current) | Err(current) => current,
},
Ordering::Equal => start.len(),
Ordering::Greater => match end.binary_search_by(|meta| meta.timestamp.cmp(&to)) {
Ok(current) | Err(current) => start.len() + current,
},
},
};
self.go_to(current)
}
#[inline]
pub fn extend(&mut self, commands: impl IntoIterator<Item = C>) -> Result<(), C::Error> {
for command in commands {
self.apply(command)?;
}
Ok(())
}
#[inline]
pub fn checkpoint(&mut self) -> Checkpoint<Record<R, C>, C> {
Checkpoint::from(self)
}
#[inline]
pub fn queue(&mut self) -> Queue<Record<R, C>, C> {
Queue::from(self)
}
#[inline]
pub fn as_receiver(&self) -> &R {
&self.receiver
}
#[inline]
pub fn as_mut_receiver(&mut self) -> &mut R {
&mut self.receiver
}
#[inline]
pub fn into_receiver(self) -> R {
self.receiver
}
#[inline]
pub fn commands(&self) -> impl Iterator<Item = &C> {
self.commands.iter().map(|meta| &meta.command)
}
}
impl<R, C: Command<R> + ToString> Record<R, C> {
#[inline]
pub fn to_undo_string(&self) -> Option<String> {
if self.can_undo() {
Some(self.commands[self.current - 1].command.to_string())
} else {
None
}
}
#[inline]
pub fn to_redo_string(&self) -> Option<String> {
if self.can_redo() {
Some(self.commands[self.current].command.to_string())
} else {
None
}
}
#[inline]
pub fn display(&self) -> Display<Self> {
Display::from(self)
}
}
impl<R: Default, C: Command<R>> Default for Record<R, C> {
#[inline]
fn default() -> Record<R, C> {
Record::new(R::default())
}
}
impl<R, C: Command<R>> AsRef<R> for Record<R, C> {
#[inline]
fn as_ref(&self) -> &R {
self.as_receiver()
}
}
impl<R, C: Command<R>> AsMut<R> for Record<R, C> {
#[inline]
fn as_mut(&mut self) -> &mut R {
self.as_mut_receiver()
}
}
impl<R, C: Command<R>> From<R> for Record<R, C> {
#[inline]
fn from(receiver: R) -> Record<R, C> {
Record::new(receiver)
}
}
impl<R, C: Command<R>> From<History<R, C>> for Record<R, C> {
#[inline]
fn from(history: History<R, C>) -> Record<R, C> {
history.record
}
}
impl<R: fmt::Debug, C: Command<R> + fmt::Debug> fmt::Debug for Record<R, C> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Record")
.field("commands", &self.commands)
.field("receiver", &self.receiver)
.field("current", &self.current)
.field("limit", &self.limit)
.field("saved", &self.saved)
.finish()
}
}
impl<R, C: Command<R> + fmt::Display> fmt::Display for Record<R, C> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
(&self.display() as &dyn fmt::Display).fmt(f)
}
}
pub struct RecordBuilder<R, C: Command<R>> {
commands: PhantomData<C>,
receiver: PhantomData<R>,
capacity: usize,
limit: NonZeroUsize,
saved: bool,
signal: Option<Box<dyn FnMut(Signal) + Send + Sync + 'static>>,
}
impl<R, C: Command<R>> RecordBuilder<R, C> {
#[inline]
pub fn capacity(mut self, capacity: usize) -> RecordBuilder<R, C> {
self.capacity = capacity;
self
}
#[inline]
pub fn limit(mut self, limit: usize) -> RecordBuilder<R, C> {
self.limit = NonZeroUsize::new(limit).expect("limit can not be `0`");
self
}
#[inline]
pub fn saved(mut self, saved: bool) -> RecordBuilder<R, C> {
self.saved = saved;
self
}
#[inline]
pub fn connect(mut self, f: impl FnMut(Signal) + Send + Sync + 'static) -> RecordBuilder<R, C> {
self.signal = Some(Box::new(f));
self
}
#[inline]
pub fn build(self, receiver: impl Into<R>) -> Record<R, C> {
Record {
commands: VecDeque::with_capacity(self.capacity),
receiver: receiver.into(),
current: 0,
limit: self.limit,
saved: if self.saved { Some(0) } else { None },
signal: self.signal,
}
}
}
impl<R: Default, C: Command<R>> RecordBuilder<R, C> {
#[inline]
pub fn default(self) -> Record<R, C> {
self.build(R::default())
}
}
impl<R: fmt::Debug, C: Command<R> + fmt::Debug> fmt::Debug for RecordBuilder<R, C> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("RecordBuilder")
.field("receiver", &self.receiver)
.field("capacity", &self.capacity)
.field("limit", &self.limit)
.field("saved", &self.saved)
.finish()
}
}
#[cfg(test)]
mod tests {
use crate::{Command, Record};
#[derive(Debug)]
struct Add(char);
impl Command<String> for Add {
type Error = &'static str;
fn apply(&mut self, s: &mut String) -> Result<(), Self::Error> {
s.push(self.0);
Ok(())
}
fn undo(&mut self, s: &mut String) -> Result<(), Self::Error> {
self.0 = s.pop().ok_or("`s` is empty")?;
Ok(())
}
}
#[test]
fn set_limit() {
let mut record = Record::default();
record.apply(Add('a')).unwrap();
record.apply(Add('b')).unwrap();
record.apply(Add('c')).unwrap();
record.apply(Add('d')).unwrap();
record.apply(Add('e')).unwrap();
record.set_limit(3);
assert_eq!(record.current(), 3);
assert_eq!(record.limit(), 3);
assert_eq!(record.len(), 3);
assert!(record.can_undo());
assert!(!record.can_redo());
record.clear();
assert_eq!(record.set_limit(5), 5);
record.apply(Add('a')).unwrap();
record.apply(Add('b')).unwrap();
record.apply(Add('c')).unwrap();
record.apply(Add('d')).unwrap();
record.apply(Add('e')).unwrap();
record.undo().unwrap().unwrap();
record.undo().unwrap().unwrap();
record.undo().unwrap().unwrap();
record.set_limit(2);
assert_eq!(record.current(), 0);
assert_eq!(record.limit(), 3);
assert_eq!(record.len(), 3);
assert!(!record.can_undo());
assert!(record.can_redo());
record.redo().unwrap().unwrap();
record.redo().unwrap().unwrap();
record.redo().unwrap().unwrap();
record.clear();
assert_eq!(record.set_limit(5), 5);
record.apply(Add('a')).unwrap();
record.apply(Add('b')).unwrap();
record.apply(Add('c')).unwrap();
record.apply(Add('d')).unwrap();
record.apply(Add('e')).unwrap();
record.undo().unwrap().unwrap();
record.undo().unwrap().unwrap();
record.undo().unwrap().unwrap();
record.undo().unwrap().unwrap();
record.undo().unwrap().unwrap();
record.set_limit(2);
assert_eq!(record.current(), 0);
assert_eq!(record.limit(), 5);
assert_eq!(record.len(), 5);
assert!(!record.can_undo());
assert!(record.can_redo());
record.redo().unwrap().unwrap();
record.redo().unwrap().unwrap();
record.redo().unwrap().unwrap();
record.redo().unwrap().unwrap();
record.redo().unwrap().unwrap();
}
#[test]
fn go_to() {
let mut record = Record::default();
record.apply(Add('a')).unwrap();
record.apply(Add('b')).unwrap();
record.apply(Add('c')).unwrap();
record.apply(Add('d')).unwrap();
record.apply(Add('e')).unwrap();
record.go_to(0).unwrap().unwrap();
assert_eq!(record.current(), 0);
assert_eq!(record.as_receiver(), "");
record.go_to(5).unwrap().unwrap();
assert_eq!(record.current(), 5);
assert_eq!(record.as_receiver(), "abcde");
record.go_to(1).unwrap().unwrap();
assert_eq!(record.current(), 1);
assert_eq!(record.as_receiver(), "a");
record.go_to(4).unwrap().unwrap();
assert_eq!(record.current(), 4);
assert_eq!(record.as_receiver(), "abcd");
record.go_to(2).unwrap().unwrap();
assert_eq!(record.current(), 2);
assert_eq!(record.as_receiver(), "ab");
record.go_to(3).unwrap().unwrap();
assert_eq!(record.current(), 3);
assert_eq!(record.as_receiver(), "abc");
assert!(record.go_to(6).is_none());
assert_eq!(record.current(), 3);
}
}