#![allow(dead_code)]
#[cfg(feature = "alloc")]
use crate::format::Format;
use crate::{At, Command, Entry, Merge, Result};
#[cfg(feature = "alloc")]
use alloc::string::{String, ToString};
use arrayvec::ArrayVec;
#[cfg(feature = "chrono")]
use chrono::Utc;
#[cfg(feature = "chrono")]
use chrono::{DateTime, TimeZone};
use core::convert::identity;
use core::fmt::{self, Write};
#[cfg(feature = "serde")]
use serde_crate::{Deserialize, Serialize};
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(
crate = "serde_crate",
bound(serialize = "C: Serialize", deserialize = "C: Deserialize<'de>")
)
)]
#[derive(Clone)]
pub struct Timeline<C, const LIMIT: usize> {
entries: ArrayVec<Entry<C>, LIMIT>,
current: usize,
saved: Option<usize>,
}
impl<C, const LIMIT: usize> Timeline<C, LIMIT> {
pub fn new() -> Timeline<C, LIMIT> {
Timeline {
entries: ArrayVec::new(),
current: 0,
saved: Some(0),
}
}
}
impl<C, const LIMIT: usize> Timeline<C, LIMIT> {
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn can_undo(&self) -> bool {
self.current() > 0
}
pub fn can_redo(&self) -> bool {
self.current() < self.len()
}
pub fn is_saved(&self) -> bool {
self.saved.map_or(false, |saved| saved == self.current())
}
pub fn current(&self) -> usize {
self.current
}
#[cfg(feature = "alloc")]
pub fn display(&self) -> Display<C, LIMIT> {
Display::from(self)
}
}
impl<C: Command, const LIMIT: usize> Timeline<C, LIMIT> {
pub fn apply(&mut self, target: &mut C::Target, mut command: C) -> Result<C> {
command.apply(target)?;
let current = self.current();
let was_saved = self.is_saved();
self.entries.truncate(current);
self.saved = self.saved.filter(|&saved| saved <= current);
let merged = match self.entries.last_mut() {
Some(last) if !was_saved => last.command.merge(command),
_ => Merge::No(command),
};
match merged {
Merge::Yes => (),
Merge::Annul => {
self.entries.pop();
}
Merge::No(command) => {
if LIMIT == self.current() {
self.entries.pop_at(0);
self.saved = self.saved.and_then(|saved| saved.checked_sub(1));
} else {
self.current += 1;
}
self.entries.push(Entry::from(command));
}
};
Ok(())
}
pub fn undo(&mut self, target: &mut C::Target) -> Result<C> {
if !self.can_undo() {
return Ok(());
}
self.entries[self.current - 1].undo(target)?;
self.current -= 1;
Ok(())
}
pub fn redo(&mut self, target: &mut C::Target) -> Result<C> {
if !self.can_redo() {
return Ok(());
}
self.entries[self.current].redo(target)?;
self.current += 1;
Ok(())
}
pub fn go_to(&mut self, target: &mut C::Target, current: usize) -> Option<Result<C>> {
if current > self.len() {
return None;
}
let apply = if current > self.current() {
Timeline::redo
} else {
Timeline::undo
};
while self.current() != current {
if let Err(err) = apply(self, target) {
return Some(Err(err));
}
}
Some(Ok(()))
}
#[cfg(feature = "chrono")]
pub fn time_travel(
&mut self,
target: &mut C::Target,
to: &DateTime<impl TimeZone>,
) -> Option<Result<C>> {
let to = to.with_timezone(&Utc);
let current = self
.entries
.binary_search_by(|e| e.timestamp.cmp(&to))
.unwrap_or_else(identity);
self.go_to(target, current)
}
pub fn set_saved(&mut self, saved: bool) {
self.saved = saved.then(|| self.current());
}
pub fn revert(&mut self, target: &mut C::Target) -> Option<Result<C>> {
self.saved.and_then(|saved| self.go_to(target, saved))
}
pub fn clear(&mut self) {
self.entries.clear();
self.saved = self.is_saved().then(|| 0);
self.current = 0;
}
}
#[cfg(feature = "alloc")]
impl<C: ToString, const LIMIT: usize> Timeline<C, LIMIT> {
pub fn undo_text(&self) -> Option<String> {
self.current.checked_sub(1).and_then(|i| self.text(i))
}
pub fn redo_text(&self) -> Option<String> {
self.text(self.current)
}
fn text(&self, i: usize) -> Option<String> {
self.entries.get(i).map(|e| e.command.to_string())
}
}
impl<C, const LIMIT: usize> Default for Timeline<C, LIMIT> {
fn default() -> Timeline<C, LIMIT> {
Timeline::new()
}
}
impl<C: fmt::Debug, const LIMIT: usize> fmt::Debug for Timeline<C, LIMIT> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Timeline")
.field("entries", &self.entries)
.field("current", &self.current)
.field("saved", &self.saved)
.finish()
}
}
#[cfg(feature = "alloc")]
pub struct Display<'a, C, const LIMIT: usize> {
timeline: &'a Timeline<C, LIMIT>,
format: Format,
}
#[cfg(feature = "alloc")]
impl<C, const LIMIT: usize> Display<'_, C, LIMIT> {
#[cfg(feature = "colored")]
pub fn colored(&mut self, on: bool) -> &mut Self {
self.format.colored = on;
self
}
pub fn current(&mut self, on: bool) -> &mut Self {
self.format.current = on;
self
}
pub fn detailed(&mut self, on: bool) -> &mut Self {
self.format.detailed = on;
self
}
pub fn position(&mut self, on: bool) -> &mut Self {
self.format.position = on;
self
}
pub fn saved(&mut self, on: bool) -> &mut Self {
self.format.saved = on;
self
}
}
#[cfg(feature = "alloc")]
impl<C: fmt::Display, const LIMIT: usize> Display<'_, C, LIMIT> {
fn fmt_list(&self, f: &mut fmt::Formatter, at: At, entry: Option<&Entry<C>>) -> fmt::Result {
self.format.position(f, at, false)?;
#[cfg(feature = "chrono")]
if let Some(entry) = entry {
if self.format.detailed {
self.format.timestamp(f, &entry.timestamp)?;
}
}
self.format.labels(
f,
at,
At::new(0, self.timeline.current()),
self.timeline.saved.map(|saved| At::new(0, saved)),
)?;
if let Some(entry) = entry {
if self.format.detailed {
writeln!(f)?;
self.format.message(f, entry, None)?;
} else {
f.write_char(' ')?;
self.format.message(f, entry, None)?;
writeln!(f)?;
}
}
Ok(())
}
}
#[cfg(feature = "alloc")]
impl<'a, C, const LIMIT: usize> From<&'a Timeline<C, LIMIT>> for Display<'a, C, LIMIT> {
fn from(timeline: &'a Timeline<C, LIMIT>) -> Self {
Display {
timeline,
format: Format::default(),
}
}
}
#[cfg(feature = "alloc")]
impl<C: fmt::Display, const LIMIT: usize> fmt::Display for Display<'_, C, LIMIT> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (i, entry) in self.timeline.entries.iter().enumerate().rev() {
let at = At::new(0, i + 1);
self.fmt_list(f, at, Some(entry))?;
}
self.fmt_list(f, At::new(0, 0), None)
}
}
#[cfg(test)]
mod tests {
use crate::*;
use arrayvec::ArrayString;
struct Add(char);
impl Command for Add {
type Target = ArrayString<64>;
type Error = &'static str;
fn apply(&mut self, s: &mut ArrayString<64>) -> Result<Add> {
s.push(self.0);
Ok(())
}
fn undo(&mut self, s: &mut ArrayString<64>) -> Result<Add> {
self.0 = s.pop().ok_or("s is empty")?;
Ok(())
}
}
#[test]
fn limit() {
let mut target = ArrayString::new();
let mut timeline = Timeline::<_, 32>::new();
for i in 64..128 {
timeline.apply(&mut target, Add(char::from(i))).unwrap();
}
assert_eq!(target.len(), 64);
assert_eq!(timeline.len(), 32);
}
}