1use crate::seq::Shift;
2use patterns_macros::because;
3use std::collections::BTreeMap;
4
5pub trait Fresh<K> {
6 fn fresh(key: &K) -> Self;
7}
8because!(Fresh, "how a row is made for a key a table has never seen, declared by the spec that owns the row, so a table can move a row that does not exist yet");
9
10pub fn touched<M, K: Ord + Clone, V: Shift<M> + Fresh<K>>(map: BTreeMap<K, V>, key: K, by: V::By) -> BTreeMap<K, V> {
11 let mut out = map;
12 let current = out.remove(&key).unwrap_or_else(|| V::fresh(&key));
13 out.insert(key, current.shifted(by));
14 out
15}
16because!(touched, "one row of a keyed table moved by a step, made fresh first when the key is new, which is how an event reaches one account among thousands without the table being rebuilt");
17
18pub fn touched_maybe<M, K: Ord + Clone, V: Shift<M> + Fresh<K>>(map: BTreeMap<K, V>, by: &Option<(K, V::By)>) -> BTreeMap<K, V> {
19 match by {
20 Some((key, step)) => touched(map, key.clone(), *step),
21 None => map,
22 }
23}
24because!(touched_maybe, "touched when an event names a row to move and the table unchanged when it does not, so a reducer with an optional touch needs no match of its own");
25
26pub fn touched_all<M, K: Ord + Clone, V: Shift<M> + Fresh<K>>(map: BTreeMap<K, V>, steps: &[(K, V::By)]) -> BTreeMap<K, V> {
27 let mut out = map;
28 for (key, step) in steps {
29 out = touched(out, key.clone(), *step);
30 }
31 out
32}
33because!(touched_all, "several rows moved in order by one event, for an event that reaches two accounts at once, the sender and the receiver");
34
35pub fn looked_or<K: Ord, V: Clone>(map: &BTreeMap<K, V>, key: &K, fallback: V) -> V {
36 map.get(key).cloned().unwrap_or(fallback)
37}
38because!(looked_or, "one entry of a keyed table or a fallback when the key is absent, for a reducer that reads a row that may not exist yet");
39
40pub fn looked_fresh<K: Ord, V: Clone + Fresh<K>>(map: &BTreeMap<K, V>, key: &K) -> V {
41 map.get(key).cloned().unwrap_or_else(|| V::fresh(key))
42}
43because!(looked_fresh, "one row of a keyed table, or the fresh row its key would start from, so a reducer reads an account's counters before its first event as zeros without a match");