mod builder;
mod checkpoint;
mod display;
mod queue;
pub use builder::Builder;
pub use checkpoint::Checkpoint;
pub use display::Display;
pub use queue::Queue;
use crate::socket::{Slot, Socket};
use crate::{Edit, Entry, Event, History, Merged};
use alloc::collections::VecDeque;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::num::NonZeroUsize;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug)]
pub struct Record<E, S = ()> {
pub(crate) limit: NonZeroUsize,
pub(crate) index: usize,
pub(crate) saved: Option<usize>,
pub(crate) socket: Socket<S>,
pub(crate) entries: VecDeque<Entry<E>>,
}
impl<E> Record<E> {
pub fn new() -> Record<E> {
Record::builder().build()
}
}
impl<E, S> Record<E, S> {
pub fn builder() -> Builder<E, S> {
Builder::default()
}
pub fn reserve(&mut self, additional: usize) {
self.entries.reserve(additional);
}
pub fn capacity(&self) -> usize {
self.entries.capacity()
}
pub fn shrink_to_fit(&mut self) {
self.entries.shrink_to_fit();
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn limit(&self) -> usize {
self.limit.get()
}
pub fn connect(&mut self, slot: S) -> Option<S> {
self.socket.connect(Some(slot))
}
pub fn disconnect(&mut self) -> Option<S> {
self.socket.disconnect()
}
pub fn can_undo(&self) -> bool {
self.index > 0
}
pub fn can_redo(&self) -> bool {
self.index < self.len()
}
pub fn is_saved(&self) -> bool {
self.saved == Some(self.index)
}
pub fn index(&self) -> usize {
self.index
}
pub fn display(&self) -> Display<E, S> {
Display::from(self)
}
pub fn edits(&self) -> impl Iterator<Item = &E> {
self.entries.iter().map(|e| &e.edit)
}
pub fn queue(&mut self) -> Queue<E, S> {
Queue::from(self)
}
pub fn checkpoint(&mut self) -> Checkpoint<E, S> {
Checkpoint::from(self)
}
fn rm_tail(&mut self) -> VecDeque<Entry<E>> {
self.saved = self.saved.filter(|&saved| saved <= self.index);
self.entries.split_off(self.index)
}
}
impl<E: Edit, S: Slot> Record<E, S> {
pub fn edit(&mut self, target: &mut E::Target, edit: E) -> E::Output {
let (output, _, _) = self.edit_and_push(target, edit.into());
output
}
pub(crate) fn edit_and_push(
&mut self,
target: &mut E::Target,
mut entry: Entry<E>,
) -> (E::Output, bool, VecDeque<Entry<E>>) {
let output = entry.edit(target);
let (merged_or_annulled, tail) = self.push(entry);
(output, merged_or_annulled, tail)
}
pub(crate) fn redo_and_push(
&mut self,
target: &mut E::Target,
mut entry: Entry<E>,
) -> (E::Output, bool, VecDeque<Entry<E>>) {
let output = entry.redo(target);
let (merged_or_annulled, tail) = self.push(entry);
(output, merged_or_annulled, tail)
}
fn push(&mut self, entry: Entry<E>) -> (bool, VecDeque<Entry<E>>) {
let old_index = self.index;
let could_undo = self.can_undo();
let could_redo = self.can_redo();
let was_saved = self.is_saved();
let tail = self.rm_tail();
let merged = match self.entries.back_mut() {
Some(last) if !was_saved => last.merge(entry),
_ => Merged::No(entry),
};
let merged_or_annulled = match merged {
Merged::Yes => true,
Merged::Annul => {
self.entries.pop_back();
self.index -= 1;
true
}
Merged::No(entry) => {
if self.limit() == self.index {
self.entries.pop_front();
self.saved = self.saved.and_then(|saved| saved.checked_sub(1));
} else {
self.index += 1;
}
self.entries.push_back(entry);
false
}
};
self.socket.emit_if(could_redo, || Event::Redo(false));
self.socket.emit_if(!could_undo, || Event::Undo(true));
self.socket.emit_if(was_saved, || Event::Saved(false));
self.socket
.emit_if(old_index != self.index, || Event::Index(self.index));
(merged_or_annulled, tail)
}
pub fn undo(&mut self, target: &mut E::Target) -> Option<E::Output> {
self.can_undo().then(|| {
let old_index = self.index;
let was_saved = self.is_saved();
let output = self.entries[self.index - 1].undo(target);
self.index -= 1;
let is_saved = self.is_saved();
self.socket.emit_if(old_index == 1, || Event::Undo(false));
self.socket
.emit_if(old_index == self.entries.len(), || Event::Redo(true));
self.socket
.emit_if(was_saved != is_saved, || Event::Saved(is_saved));
self.socket.emit(|| Event::Index(self.index));
output
})
}
pub fn redo(&mut self, target: &mut E::Target) -> Option<E::Output> {
self.can_redo().then(|| {
let old_index = self.index;
let was_saved = self.is_saved();
let output = self.entries[self.index].redo(target);
self.index += 1;
let is_saved = self.is_saved();
self.socket.emit_if(old_index == 0, || Event::Undo(true));
self.socket
.emit_if(old_index == self.len() - 1, || Event::Redo(false));
self.socket
.emit_if(was_saved != is_saved, || Event::Saved(is_saved));
self.socket.emit(|| Event::Index(self.index));
output
})
}
pub fn set_saved(&mut self, saved: bool) {
let was_saved = self.is_saved();
if saved {
self.saved = Some(self.index);
self.socket.emit_if(!was_saved, || Event::Saved(true));
} else {
self.saved = None;
self.socket.emit_if(was_saved, || Event::Saved(false));
}
}
pub fn clear(&mut self) {
let old_index = self.index;
let could_undo = self.can_undo();
let could_redo = self.can_redo();
self.entries.clear();
self.saved = self.is_saved().then_some(0);
self.index = 0;
self.socket.emit_if(could_undo, || Event::Undo(false));
self.socket.emit_if(could_redo, || Event::Redo(false));
self.socket.emit_if(old_index != 0, || Event::Index(0));
}
pub fn revert(&mut self, target: &mut E::Target) -> Vec<E::Output> {
self.saved
.map_or_else(Vec::new, |saved| self.go_to(target, saved))
}
pub fn go_to(&mut self, target: &mut E::Target, index: usize) -> Vec<E::Output> {
if index > self.len() {
return Vec::new();
}
let old_index = self.index;
let could_undo = self.can_undo();
let could_redo = self.can_redo();
let was_saved = self.is_saved();
let slot = self.socket.disconnect();
let undo_or_redo = if index > self.index {
Record::redo
} else {
Record::undo
};
let capacity = self.index.abs_diff(index);
let mut outputs = Vec::with_capacity(capacity);
while self.index != index {
let output = undo_or_redo(self, target).unwrap();
outputs.push(output);
}
let can_undo = self.can_undo();
let can_redo = self.can_redo();
let is_saved = self.is_saved();
self.socket.connect(slot);
self.socket
.emit_if(could_undo != can_undo, || Event::Undo(can_undo));
self.socket
.emit_if(could_redo != can_redo, || Event::Redo(can_redo));
self.socket
.emit_if(was_saved != is_saved, || Event::Saved(is_saved));
self.socket
.emit_if(old_index != self.index, || Event::Index(self.index));
outputs
}
}
impl<E: ToString, S> Record<E, S> {
pub fn undo_string(&self) -> Option<String> {
self.index.checked_sub(1).and_then(|i| self.string_at(i))
}
pub fn redo_string(&self) -> Option<String> {
self.string_at(self.index)
}
fn string_at(&self, i: usize) -> Option<String> {
self.entries.get(i).map(|e| e.edit.to_string())
}
}
impl<E> Default for Record<E> {
fn default() -> Record<E> {
Record::new()
}
}
impl<E, F> From<History<E, F>> for Record<E, F> {
fn from(history: History<E, F>) -> Record<E, F> {
history.record
}
}
#[cfg(test)]
mod tests {
use crate::*;
use alloc::string::String;
use alloc::vec::Vec;
#[test]
fn go_to() {
let mut target = String::new();
let mut record = Record::new();
record.edit(&mut target, Add('a'));
record.edit(&mut target, Add('b'));
record.edit(&mut target, Add('c'));
record.edit(&mut target, Add('d'));
record.edit(&mut target, Add('e'));
record.go_to(&mut target, 0);
assert_eq!(record.index(), 0);
assert_eq!(target, "");
record.go_to(&mut target, 5);
assert_eq!(record.index(), 5);
assert_eq!(target, "abcde");
record.go_to(&mut target, 1);
assert_eq!(record.index(), 1);
assert_eq!(target, "a");
record.go_to(&mut target, 4);
assert_eq!(record.index(), 4);
assert_eq!(target, "abcd");
record.go_to(&mut target, 2);
assert_eq!(record.index(), 2);
assert_eq!(target, "ab");
record.go_to(&mut target, 3);
assert_eq!(record.index(), 3);
assert_eq!(target, "abc");
assert!(record.go_to(&mut target, 6).is_empty());
assert_eq!(record.index(), 3);
}
#[test]
fn edits() {
let mut target = String::new();
let mut record = Record::new();
record.edit(&mut target, Add('a'));
record.edit(&mut target, Add('b'));
let collected = record.edits().collect::<Vec<_>>();
assert_eq!(&collected[..], &[&Add('a'), &Add('b')][..]);
}
}