src/Reading.bend

ber-core-store@0.1.1.0 · 97 lines · 0 definitions

1import Base
2
3# Pure chain-reading cores; LAWS.bend quantifies over these, never over IO.
4# Entries are a named Data constructor because a & pair defaults to kind
5# Type, which List<&2, _> rejects.
6type ChainEntry is Data:
7  Entry{entry_key: String, entry_value: String}
8
9# Builds the "namespace/record" lookup key.
10def encode_lookup_key(+namespace_name: String, +record_identifier: String) -> String:
11  namespace_name ++ "/" ++ record_identifier
12
13# Some on match, otherwise the recursive fallback.
14def pick_match(key_matches: Bool, stored_value: String, fallback: Maybe<&2, String>) -> Maybe<&2, String>:
15  match key_matches:
16    case True{}:
17      Some{stored_value}
18    case False{}:
19      fallback
20
21# Most-recent-wins: the head of the chain is the newest commit.
22def lookup_in_chain(+namespace_name: String, +record_identifier: String, ancestor_chain: List<&2, ChainEntry>) -> Maybe<&2, String>:
23  match ancestor_chain:
24    case Nil{}:
25      None{}
26    case Con{head_entry, remaining_chain}:
27      match head_entry:
28        case Entry{entry_key, entry_value}:
29          pick_match(String.eq(entry_key, encode_lookup_key(namespace_name, record_identifier)), entry_value, lookup_in_chain(namespace_name, record_identifier, remaining_chain))
30
31# A tombstone reads as absent; any other value survives.
32def drop_tombstone(is_tombstone: Bool, stored_value: String) -> Maybe<&2, String>:
33  match is_tombstone:
34    case True{}:
35      None{}
36    case False{}:
37      Some{stored_value}
38
39# Maps the TOMBSTONE sentinel to None.
40def resolve_tombstone(maybe_value: Maybe<&2, String>) -> Maybe<&2, String>:
41  match maybe_value:
42    case None{}:
43      None{}
44    case Some{+stored_value}:
45      drop_tombstone(String.eq(stored_value, "TOMBSTONE"), stored_value)
46
47# Drops the entry on match, keeps it otherwise.
48def drop_or_keep(is_match: Bool, stored_key: String, stored_value: String, filtered_tail: List<&2, ChainEntry>) -> List<&2, ChainEntry>:
49  match is_match:
50    case True{}:
51      filtered_tail
52    case False{}:
53      Con{Entry{stored_key, stored_value}, filtered_tail}
54
55# Removes the head entry when its key equals the target.
56def filter_entry(head_entry: ChainEntry, filtered_tail: List<&2, ChainEntry>, target_key: String) -> List<&2, ChainEntry>:
57  match head_entry:
58    case Entry{+entry_key, +entry_value}:
59      drop_or_keep(String.eq(entry_key, target_key), entry_key, entry_value, filtered_tail)
60
61# Removes every entry whose key equals the target.
62def remove_key_matches(+target_key: String, ancestor_chain: List<&2, ChainEntry>) -> List<&2, ChainEntry>:
63  match ancestor_chain:
64    case Nil{}:
65      Nil{}
66    case Con{head_entry, remaining_chain}:
67      filter_entry(head_entry, remove_key_matches(target_key, remaining_chain), target_key)
68
69# Keeps the head and drops its older duplicates from the normalized tail.
70def attach_head(head_entry: ChainEntry, normalized_tail: List<&2, ChainEntry>) -> List<&2, ChainEntry>:
71  match head_entry:
72    case Entry{+entry_key, +entry_value}:
73      Con{Entry{entry_key, entry_value}, remove_key_matches(entry_key, normalized_tail)}
74
75# Drops older duplicates, keeping the first (newest) occurrence of each key.
76def normalize_chain(ancestor_chain: List<&2, ChainEntry>) -> List<&2, ChainEntry>:
77  match ancestor_chain:
78    case Nil{}:
79      Nil{}
80    case Con{head_entry, remaining_chain}:
81      attach_head(head_entry, normalize_chain(remaining_chain))
82
83# Shared vocabulary: other modules build ChainEntry through this constructor.
84def make_entry(entry_key: String, entry_value: String) -> ChainEntry:
85  Entry{entry_key, entry_value}
86
87# Projects the key out of an entry.
88def entry_key_of(chain_entry: ChainEntry) -> String:
89  match chain_entry:
90    case Entry{entry_key, entry_value}:
91      entry_key
92
93# Projects the value out of an entry.
94def entry_value_of(chain_entry: ChainEntry) -> String:
95  match chain_entry:
96    case Entry{entry_key, entry_value}:
97      entry_value