sim_lib_web_bridge/
history.rs1use std::collections::BTreeMap;
11
12use sim_kernel::{Cx, 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 cx: &mut Cx,
52 transport: &mut T,
53 resource: &Symbol,
54 new_value: Expr,
55 ) -> Result<()> {
56 let old_value = transport.read(cx, resource)?;
57 transport.realize(cx, resource, &set_value_op(new_value.clone()))?;
58 self.past.push(LedgerEntry {
59 resource: resource.clone(),
60 forward: set_value_op(new_value),
61 inverse: set_value_op(old_value),
62 });
63 self.future.clear();
64 Ok(())
65 }
66
67 pub fn undo<T: Transport>(&mut self, cx: &mut Cx, transport: &mut T) -> Result<Option<Symbol>> {
70 let Some(entry) = self.past.pop() else {
71 return Ok(None);
72 };
73 transport.realize(cx, &entry.resource, &entry.inverse)?;
74 let resource = entry.resource.clone();
75 self.future.push(entry);
76 Ok(Some(resource))
77 }
78
79 pub fn redo<T: Transport>(&mut self, cx: &mut Cx, transport: &mut T) -> Result<Option<Symbol>> {
81 let Some(entry) = self.future.pop() else {
82 return Ok(None);
83 };
84 transport.realize(cx, &entry.resource, &entry.forward)?;
85 let resource = entry.resource.clone();
86 self.past.push(entry);
87 Ok(Some(resource))
88 }
89
90 pub fn can_undo(&self) -> bool {
92 !self.past.is_empty()
93 }
94
95 pub fn can_redo(&self) -> bool {
97 !self.future.is_empty()
98 }
99
100 pub fn as_value(&self) -> Expr {
103 Expr::List(
104 self.past
105 .iter()
106 .map(|entry| {
107 Expr::Map(vec![
108 (
109 Expr::Symbol(Symbol::new("resource")),
110 Expr::Symbol(entry.resource.clone()),
111 ),
112 (Expr::Symbol(Symbol::new("op")), entry.forward.clone()),
113 (Expr::Symbol(Symbol::new("inverse")), entry.inverse.clone()),
114 ])
115 })
116 .collect(),
117 )
118 }
119}
120
121#[derive(Default)]
123pub struct Snapshots {
124 named: BTreeMap<String, Expr>,
125 order: Vec<String>,
126}
127
128impl Snapshots {
129 pub fn new() -> Self {
131 Self::default()
132 }
133
134 pub fn take(&mut self, name: &str, value: Expr) {
136 if !self.named.contains_key(name) {
137 self.order.push(name.to_owned());
138 }
139 self.named.insert(name.to_owned(), value);
140 }
141
142 pub fn restore(&self, name: &str) -> Option<Expr> {
144 self.named.get(name).cloned()
145 }
146
147 pub fn names(&self) -> &[String] {
149 &self.order
150 }
151}
152
153#[derive(Default)]
155pub struct SessionLog {
156 events: Vec<Expr>,
157}
158
159impl SessionLog {
160 pub fn new() -> Self {
162 Self::default()
163 }
164
165 pub fn append(&mut self, event: Expr) {
167 self.events.push(event);
168 }
169
170 pub fn as_value(&self) -> Expr {
172 Expr::List(self.events.clone())
173 }
174
175 pub fn len(&self) -> usize {
177 self.events.len()
178 }
179
180 pub fn is_empty(&self) -> bool {
182 self.events.is_empty()
183 }
184}
185
186pub fn annotate(object: &Expr, author: &str, comment: &str) -> Result<Expr> {
189 let Expr::Map(entries) = object else {
190 return Err(Error::HostError(
191 "annotations attach to map-shaped objects".to_owned(),
192 ));
193 };
194 let mut entries = entries.clone();
195 let annotation = Expr::Map(vec![
196 (
197 Expr::Symbol(Symbol::new("author")),
198 Expr::Symbol(Symbol::new(author)),
199 ),
200 (
201 Expr::Symbol(Symbol::new("text")),
202 Expr::String(comment.to_owned()),
203 ),
204 ]);
205 let key = Expr::Symbol(Symbol::new("annotations"));
206 if let Some(slot) = entries.iter_mut().find(|(entry_key, _)| entry_key == &key) {
207 if let Expr::List(list) = &mut slot.1 {
208 list.push(annotation);
209 } else {
210 slot.1 = Expr::List(vec![annotation]);
211 }
212 } else {
213 entries.push((key, Expr::List(vec![annotation])));
214 }
215 Ok(Expr::Map(entries))
216}