sim_lib_web_bridge/
history.rs1use std::collections::BTreeMap;
11
12use sim_kernel::{Error, Expr, Result, Symbol};
13
14use crate::transport::Transport;
15
16fn set_value_op(value: Expr) -> Expr {
17 Expr::Map(vec![
18 (
19 Expr::Symbol(Symbol::new("op")),
20 Expr::Symbol(Symbol::new("set-value")),
21 ),
22 (Expr::Symbol(Symbol::new("value")), value),
23 ])
24}
25
26#[derive(Clone, Debug)]
28struct LedgerEntry {
29 resource: Symbol,
30 forward: Expr,
31 inverse: Expr,
32}
33
34#[derive(Default)]
36pub struct History {
37 past: Vec<LedgerEntry>,
38 future: Vec<LedgerEntry>,
39}
40
41impl History {
42 pub fn new() -> Self {
44 Self::default()
45 }
46
47 pub fn commit<T: Transport>(
50 &mut self,
51 transport: &mut T,
52 resource: &Symbol,
53 new_value: Expr,
54 ) -> Result<()> {
55 let old_value = transport.read(resource)?;
56 transport.realize(resource, &set_value_op(new_value.clone()))?;
57 self.past.push(LedgerEntry {
58 resource: resource.clone(),
59 forward: set_value_op(new_value),
60 inverse: set_value_op(old_value),
61 });
62 self.future.clear();
63 Ok(())
64 }
65
66 pub fn undo<T: Transport>(&mut self, transport: &mut T) -> Result<Option<Symbol>> {
69 let Some(entry) = self.past.pop() else {
70 return Ok(None);
71 };
72 transport.realize(&entry.resource, &entry.inverse)?;
73 let resource = entry.resource.clone();
74 self.future.push(entry);
75 Ok(Some(resource))
76 }
77
78 pub fn redo<T: Transport>(&mut self, transport: &mut T) -> Result<Option<Symbol>> {
80 let Some(entry) = self.future.pop() else {
81 return Ok(None);
82 };
83 transport.realize(&entry.resource, &entry.forward)?;
84 let resource = entry.resource.clone();
85 self.past.push(entry);
86 Ok(Some(resource))
87 }
88
89 pub fn can_undo(&self) -> bool {
91 !self.past.is_empty()
92 }
93
94 pub fn can_redo(&self) -> bool {
96 !self.future.is_empty()
97 }
98
99 pub fn as_value(&self) -> Expr {
102 Expr::List(
103 self.past
104 .iter()
105 .map(|entry| {
106 Expr::Map(vec![
107 (
108 Expr::Symbol(Symbol::new("resource")),
109 Expr::Symbol(entry.resource.clone()),
110 ),
111 (Expr::Symbol(Symbol::new("op")), entry.forward.clone()),
112 (Expr::Symbol(Symbol::new("inverse")), entry.inverse.clone()),
113 ])
114 })
115 .collect(),
116 )
117 }
118}
119
120#[derive(Default)]
122pub struct Snapshots {
123 named: BTreeMap<String, Expr>,
124 order: Vec<String>,
125}
126
127impl Snapshots {
128 pub fn new() -> Self {
130 Self::default()
131 }
132
133 pub fn take(&mut self, name: &str, value: Expr) {
135 if !self.named.contains_key(name) {
136 self.order.push(name.to_owned());
137 }
138 self.named.insert(name.to_owned(), value);
139 }
140
141 pub fn restore(&self, name: &str) -> Option<Expr> {
143 self.named.get(name).cloned()
144 }
145
146 pub fn names(&self) -> &[String] {
148 &self.order
149 }
150}
151
152#[derive(Default)]
154pub struct SessionLog {
155 events: Vec<Expr>,
156}
157
158impl SessionLog {
159 pub fn new() -> Self {
161 Self::default()
162 }
163
164 pub fn append(&mut self, event: Expr) {
166 self.events.push(event);
167 }
168
169 pub fn as_value(&self) -> Expr {
171 Expr::List(self.events.clone())
172 }
173
174 pub fn len(&self) -> usize {
176 self.events.len()
177 }
178
179 pub fn is_empty(&self) -> bool {
181 self.events.is_empty()
182 }
183}
184
185pub fn annotate(object: &Expr, author: &str, comment: &str) -> Result<Expr> {
188 let Expr::Map(entries) = object else {
189 return Err(Error::HostError(
190 "annotations attach to map-shaped objects".to_owned(),
191 ));
192 };
193 let mut entries = entries.clone();
194 let annotation = Expr::Map(vec![
195 (
196 Expr::Symbol(Symbol::new("author")),
197 Expr::Symbol(Symbol::new(author)),
198 ),
199 (
200 Expr::Symbol(Symbol::new("text")),
201 Expr::String(comment.to_owned()),
202 ),
203 ]);
204 let key = Expr::Symbol(Symbol::new("annotations"));
205 if let Some(slot) = entries.iter_mut().find(|(entry_key, _)| entry_key == &key) {
206 if let Expr::List(list) = &mut slot.1 {
207 list.push(annotation);
208 } else {
209 slot.1 = Expr::List(vec![annotation]);
210 }
211 } else {
212 entries.push((key, Expr::List(vec![annotation])));
213 }
214 Ok(Expr::Map(entries))
215}