1use crate::{
7 datum::Datum,
8 datum_store::DatumStore,
9 effect::{Effect, EffectRecord},
10 error::{Error, Result},
11 event::{Event, EventKind, Tick},
12 event_ledger::EventLedger,
13 expr::NumberLiteral,
14 id::Symbol,
15 ref_id::{ContentId, Coordinate, HandleId, Ref},
16};
17
18#[derive(Clone, Debug)]
20pub struct EffectLedger {
21 run: Ref,
22 events: EventLedger,
23 records: Vec<EffectRecord>,
24 effects_by_ref: Vec<(Ref, Effect)>,
25 cassette_results: Vec<(ContentId, Ref)>,
26}
27
28impl Default for EffectLedger {
29 fn default() -> Self {
30 Self::new()
31 }
32}
33
34impl EffectLedger {
35 pub fn new() -> Self {
37 Self::with_run(Ref::Symbol(Symbol::qualified("effect", "ledger")))
38 }
39
40 pub fn with_run(run: Ref) -> Self {
42 Self {
43 run,
44 events: EventLedger::new(),
45 records: Vec::new(),
46 effects_by_ref: Vec::new(),
47 cassette_results: Vec::new(),
48 }
49 }
50
51 pub fn records(&self) -> &[EffectRecord] {
53 &self.records
54 }
55
56 pub fn events(&self) -> &EventLedger {
58 &self.events
59 }
60
61 pub fn events_for_run(&self) -> &[Event] {
63 self.events.events_for_run(&self.run)
64 }
65
66 pub fn cassette_result(&self, key: &ContentId) -> Option<&Ref> {
68 self.cassette_results
69 .iter()
70 .rev()
71 .find_map(|(recorded_key, result)| (recorded_key == key).then_some(result))
72 }
73
74 pub fn insert_cassette_result(&mut self, key: ContentId, result: Ref) {
76 self.cassette_results.push((key, result));
77 }
78
79 pub fn effect(&self, reference: &Ref) -> Option<&Effect> {
81 self.effects_by_ref
82 .iter()
83 .rev()
84 .find_map(|(effect_ref, effect)| (effect_ref == reference).then_some(effect))
85 }
86
87 pub fn record_requested(
90 &mut self,
91 datum_store: &mut impl DatumStore,
92 effect: Effect,
93 ) -> Result<EffectRecord> {
94 let effect_ref = effect.id.clone();
95 let event = self.events.push(
96 self.run.clone(),
97 EventKind::EffectRequested {
98 effect: effect_ref.clone(),
99 },
100 )?;
101 let requested_event = event_ref(datum_store, &event)?;
102 let record = EffectRecord {
103 effect: effect_ref.clone(),
104 requested_event,
105 resolved_event: None,
106 result: None,
107 aborted: false,
108 };
109 self.effects_by_ref.push((effect_ref, effect));
110 self.records.push(record.clone());
111 Ok(record)
112 }
113
114 pub fn record_resolved(
117 &mut self,
118 datum_store: &mut impl DatumStore,
119 effect: Ref,
120 result: Ref,
121 ) -> Result<EffectRecord> {
122 let event = self.events.push(
123 self.run.clone(),
124 EventKind::EffectResolved {
125 effect: effect.clone(),
126 result: result.clone(),
127 },
128 )?;
129 let resolved_event = event_ref(datum_store, &event)?;
130 let record = self.open_record_mut(&effect)?;
131 record.resolved_event = Some(resolved_event);
132 record.result = Some(result.clone());
133 Ok(record.clone())
134 }
135
136 pub fn record_failed(
139 &mut self,
140 datum_store: &mut impl DatumStore,
141 effect: Ref,
142 error: Ref,
143 ) -> Result<EffectRecord> {
144 let event = self
145 .events
146 .push(self.run.clone(), EventKind::Failed(error))?;
147 let failed_event = event_ref(datum_store, &event)?;
148 let record = self.open_record_mut(&effect)?;
149 record.resolved_event = Some(failed_event);
150 record.aborted = true;
151 Ok(record.clone())
152 }
153
154 fn open_record_mut(&mut self, effect: &Ref) -> Result<&mut EffectRecord> {
155 self.records
156 .iter_mut()
157 .rev()
158 .find(|record| &record.effect == effect && record.resolved_event.is_none())
159 .ok_or_else(|| Error::Eval(format!("effect record not found for {effect:?}")))
160 }
161}
162
163fn event_ref(datum_store: &mut impl DatumStore, event: &Event) -> Result<Ref> {
164 let id = datum_store.intern(event_datum(event))?;
165 Ok(Ref::Content(id))
166}
167
168fn event_datum(event: &Event) -> Datum {
169 Datum::Node {
170 tag: core_symbol("Event"),
171 fields: vec![
172 (Symbol::new("run"), ref_datum(event.run.clone())),
173 (Symbol::new("seq"), u64_datum(event.seq)),
174 (
175 Symbol::new("ticks"),
176 Datum::List(event.ticks.iter().map(tick_datum).collect()),
177 ),
178 (Symbol::new("kind"), event_kind_datum(&event.kind)),
179 ],
180 }
181}
182
183fn tick_datum(tick: &Tick) -> Datum {
184 Datum::Node {
185 tag: core_symbol("Tick"),
186 fields: vec![
187 (Symbol::new("clock"), Datum::Symbol(tick.clock.clone())),
188 (Symbol::new("index"), ref_datum(tick.index.clone())),
189 ],
190 }
191}
192
193fn event_kind_datum(kind: &EventKind) -> Datum {
194 match kind {
195 EventKind::Started { request } => tagged_ref("Started", "request", request.clone()),
196 EventKind::Claim { claim } => tagged_ref("Claim", "claim", claim.clone()),
197 EventKind::Diagnostic(diagnostic) => Datum::Node {
198 tag: core_symbol("Diagnostic"),
199 fields: vec![(
200 Symbol::new("debug"),
201 Datum::String(format!("{diagnostic:?}")),
202 )],
203 },
204 EventKind::Trace(trace) => tagged_ref("Trace", "trace", trace.clone()),
205 EventKind::Chunk { payload } => tagged_ref("Chunk", "payload", payload.clone()),
206 EventKind::EffectRequested { effect } => {
207 tagged_ref("EffectRequested", "effect", effect.clone())
208 }
209 EventKind::EffectResolved { effect, result } => Datum::Node {
210 tag: core_symbol("EffectResolved"),
211 fields: vec![
212 (Symbol::new("effect"), ref_datum(effect.clone())),
213 (Symbol::new("result"), ref_datum(result.clone())),
214 ],
215 },
216 EventKind::Capture { effect } => tagged_ref("Capture", "effect", effect.clone()),
217 EventKind::Card { subject, card } => Datum::Node {
218 tag: core_symbol("Card"),
219 fields: vec![
220 (Symbol::new("subject"), ref_datum(subject.clone())),
221 (Symbol::new("card"), ref_datum(card.clone())),
222 ],
223 },
224 EventKind::Final(value) => tagged_ref("Final", "value", value.clone()),
225 EventKind::Failed(error) => tagged_ref("Failed", "error", error.clone()),
226 EventKind::Done => Datum::Node {
227 tag: core_symbol("Done"),
228 fields: Vec::new(),
229 },
230 }
231}
232
233fn tagged_ref(tag: &str, field: &str, reference: Ref) -> Datum {
234 Datum::Node {
235 tag: core_symbol(tag),
236 fields: vec![(Symbol::new(field), ref_datum(reference))],
237 }
238}
239
240fn ref_datum(reference: Ref) -> Datum {
241 match reference {
242 Ref::Symbol(symbol) => Datum::Node {
243 tag: core_symbol("ref"),
244 fields: vec![
245 (Symbol::new("kind"), Datum::Symbol(core_symbol("symbol"))),
246 (Symbol::new("symbol"), Datum::Symbol(symbol)),
247 ],
248 },
249 Ref::Content(content) => Datum::Node {
250 tag: core_symbol("ref"),
251 fields: vec![
252 (Symbol::new("kind"), Datum::Symbol(core_symbol("content"))),
253 (Symbol::new("content"), content_id_datum(content)),
254 ],
255 },
256 Ref::Handle(handle) => Datum::Node {
257 tag: core_symbol("ref"),
258 fields: vec![
259 (Symbol::new("kind"), Datum::Symbol(core_symbol("handle"))),
260 (Symbol::new("id"), handle_id_datum(handle)),
261 ],
262 },
263 Ref::Coord(coordinate) => coordinate_datum(coordinate),
264 }
265}
266
267fn coordinate_datum(coordinate: Coordinate) -> Datum {
268 Datum::Node {
269 tag: core_symbol("ref"),
270 fields: vec![
271 (Symbol::new("kind"), Datum::Symbol(core_symbol("coord"))),
272 (Symbol::new("space"), Datum::Symbol(coordinate.space)),
273 (Symbol::new("ordinal"), content_id_datum(coordinate.ordinal)),
274 ],
275 }
276}
277
278fn content_id_datum(content: ContentId) -> Datum {
279 Datum::Node {
280 tag: core_symbol("content-id"),
281 fields: vec![
282 (Symbol::new("algorithm"), Datum::Symbol(content.algorithm)),
283 (Symbol::new("bytes"), Datum::Bytes(content.bytes.to_vec())),
284 ],
285 }
286}
287
288fn handle_id_datum(handle: HandleId) -> Datum {
289 Datum::Bytes(handle.0.to_be_bytes().to_vec())
290}
291
292fn u64_datum(value: u64) -> Datum {
293 Datum::Number(NumberLiteral {
294 domain: core_symbol("u64"),
295 canonical: value.to_string(),
296 })
297}
298
299fn core_symbol(name: &str) -> Symbol {
300 Symbol::qualified("core", name)
301}