use std::collections::BTreeMap;
use sim_kernel::{Error, Expr, Result, Symbol};
use crate::transport::Transport;
fn set_value_op(value: Expr) -> Expr {
Expr::Map(vec![
(
Expr::Symbol(Symbol::new("op")),
Expr::Symbol(Symbol::new("set-value")),
),
(Expr::Symbol(Symbol::new("value")), value),
])
}
#[derive(Clone, Debug)]
struct LedgerEntry {
resource: Symbol,
forward: Expr,
inverse: Expr,
}
#[derive(Default)]
pub struct History {
past: Vec<LedgerEntry>,
future: Vec<LedgerEntry>,
}
impl History {
pub fn new() -> Self {
Self::default()
}
pub fn commit<T: Transport>(
&mut self,
transport: &mut T,
resource: &Symbol,
new_value: Expr,
) -> Result<()> {
let old_value = transport.read(resource)?;
transport.realize(resource, &set_value_op(new_value.clone()))?;
self.past.push(LedgerEntry {
resource: resource.clone(),
forward: set_value_op(new_value),
inverse: set_value_op(old_value),
});
self.future.clear();
Ok(())
}
pub fn undo<T: Transport>(&mut self, transport: &mut T) -> Result<Option<Symbol>> {
let Some(entry) = self.past.pop() else {
return Ok(None);
};
transport.realize(&entry.resource, &entry.inverse)?;
let resource = entry.resource.clone();
self.future.push(entry);
Ok(Some(resource))
}
pub fn redo<T: Transport>(&mut self, transport: &mut T) -> Result<Option<Symbol>> {
let Some(entry) = self.future.pop() else {
return Ok(None);
};
transport.realize(&entry.resource, &entry.forward)?;
let resource = entry.resource.clone();
self.past.push(entry);
Ok(Some(resource))
}
pub fn can_undo(&self) -> bool {
!self.past.is_empty()
}
pub fn can_redo(&self) -> bool {
!self.future.is_empty()
}
pub fn as_value(&self) -> Expr {
Expr::List(
self.past
.iter()
.map(|entry| {
Expr::Map(vec![
(
Expr::Symbol(Symbol::new("resource")),
Expr::Symbol(entry.resource.clone()),
),
(Expr::Symbol(Symbol::new("op")), entry.forward.clone()),
(Expr::Symbol(Symbol::new("inverse")), entry.inverse.clone()),
])
})
.collect(),
)
}
}
#[derive(Default)]
pub struct Snapshots {
named: BTreeMap<String, Expr>,
order: Vec<String>,
}
impl Snapshots {
pub fn new() -> Self {
Self::default()
}
pub fn take(&mut self, name: &str, value: Expr) {
if !self.named.contains_key(name) {
self.order.push(name.to_owned());
}
self.named.insert(name.to_owned(), value);
}
pub fn restore(&self, name: &str) -> Option<Expr> {
self.named.get(name).cloned()
}
pub fn names(&self) -> &[String] {
&self.order
}
}
#[derive(Default)]
pub struct SessionLog {
events: Vec<Expr>,
}
impl SessionLog {
pub fn new() -> Self {
Self::default()
}
pub fn append(&mut self, event: Expr) {
self.events.push(event);
}
pub fn as_value(&self) -> Expr {
Expr::List(self.events.clone())
}
pub fn len(&self) -> usize {
self.events.len()
}
pub fn is_empty(&self) -> bool {
self.events.is_empty()
}
}
pub fn annotate(object: &Expr, author: &str, comment: &str) -> Result<Expr> {
let Expr::Map(entries) = object else {
return Err(Error::HostError(
"annotations attach to map-shaped objects".to_owned(),
));
};
let mut entries = entries.clone();
let annotation = Expr::Map(vec![
(
Expr::Symbol(Symbol::new("author")),
Expr::Symbol(Symbol::new(author)),
),
(
Expr::Symbol(Symbol::new("text")),
Expr::String(comment.to_owned()),
),
]);
let key = Expr::Symbol(Symbol::new("annotations"));
if let Some(slot) = entries.iter_mut().find(|(entry_key, _)| entry_key == &key) {
if let Expr::List(list) = &mut slot.1 {
list.push(annotation);
} else {
slot.1 = Expr::List(vec![annotation]);
}
} else {
entries.push((key, Expr::List(vec![annotation])));
}
Ok(Expr::Map(entries))
}