1use std::collections::{BTreeMap, BTreeSet};
2use std::sync::{Arc, Mutex};
3
4use teaql_core::{Record, Value};
5
6#[derive(Debug, Clone)]
7pub struct EntityKey {
8 pub entity: String,
9 pub id: Value,
10 id_key: String,
11}
12
13impl EntityKey {
14 pub fn new(entity: impl Into<String>, id: impl Into<Value>) -> Self {
15 let id = id.into();
16 Self {
17 entity: entity.into(),
18 id_key: value_key(&id),
19 id,
20 }
21 }
22}
23
24impl PartialEq for EntityKey {
25 fn eq(&self, other: &Self) -> bool {
26 self.entity == other.entity && self.id_key == other.id_key
27 }
28}
29
30impl Eq for EntityKey {}
31
32impl PartialOrd for EntityKey {
33 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
34 Some(self.cmp(other))
35 }
36}
37
38impl Ord for EntityKey {
39 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
40 self.entity
41 .cmp(&other.entity)
42 .then_with(|| self.id_key.cmp(&other.id_key))
43 }
44}
45
46fn value_key(value: &Value) -> String {
47 match value {
48 Value::Null => "null".to_owned(),
49 Value::Bool(value) => format!("bool:{value}"),
50 Value::I64(value) => format!("i64:{value}"),
51 Value::U64(value) => format!("u64:{value}"),
52 Value::F64(value) => format!("f64:{value}"),
53 Value::Decimal(value) => format!("decimal:{value}"),
54 Value::Text(value) => format!("text:{value}"),
55 Value::Json(value) => format!("json:{value}"),
56 Value::Date(value) => format!("date:{value}"),
57 Value::Timestamp(value) => format!("timestamp:{}", value.0),
58 Value::Object(_) => "object".to_owned(),
59 Value::List(_) => "list".to_owned(),
60 Value::TypedNull(_) => "null".to_owned(),
61 }
62}
63
64#[derive(Debug, Clone, Default, PartialEq)]
65pub struct EntityChangeSet {
66 changes: BTreeMap<EntityKey, Record>,
67}
68
69impl EntityChangeSet {
70 pub fn is_empty(&self) -> bool {
71 self.changes.is_empty()
72 }
73
74 pub fn set(&mut self, key: EntityKey, field: impl Into<String>, value: Value) {
75 self.changes
76 .entry(key)
77 .or_default()
78 .insert(field.into(), value);
79 }
80
81 pub fn get(&self, key: &EntityKey, field: &str) -> Option<&Value> {
82 self.changes.get(key).and_then(|changes| changes.get(field))
83 }
84
85 pub fn changes(&self) -> &BTreeMap<EntityKey, Record> {
86 &self.changes
87 }
88
89 pub fn clear_entity(&mut self, key: &EntityKey) {
91 self.changes.remove(key);
92 }
93
94 pub fn field_names(&self, key: &EntityKey) -> BTreeSet<String> {
96 self.changes
97 .get(key)
98 .map(|record| record.keys().cloned().collect())
99 .unwrap_or_default()
100 }
101}
102
103#[derive(Debug, Clone, Default, PartialEq)]
104pub struct ChangeSetStack {
105 stack: Vec<EntityChangeSet>,
106}
107
108impl ChangeSetStack {
109 pub fn current_mut(&mut self) -> &mut EntityChangeSet {
110 if self.stack.is_empty() {
111 self.stack.push(EntityChangeSet::default());
112 }
113 self.stack.last_mut().expect("change set stack has current")
114 }
115
116 pub fn current(&self) -> Option<&EntityChangeSet> {
117 self.stack.last()
118 }
119
120 pub fn push(&mut self) {
121 self.stack.push(EntityChangeSet::default());
122 }
123
124 pub fn pop(&mut self) -> Option<EntityChangeSet> {
125 self.stack.pop()
126 }
127
128 pub fn get(&self, key: &EntityKey, field: &str) -> Option<Value> {
129 self.stack
130 .iter()
131 .rev()
132 .find_map(|change_set| change_set.get(key, field).cloned())
133 }
134
135 pub fn set(&mut self, key: EntityKey, field: impl Into<String>, value: Value) {
136 self.current_mut().set(key, field, value);
137 }
138
139 pub fn clear_current(&mut self) {
140 if let Some(current) = self.stack.last_mut() {
141 *current = EntityChangeSet::default();
142 }
143 }
144
145 pub fn clear_entity(&mut self, key: &EntityKey) {
147 for change_set in &mut self.stack {
148 change_set.clear_entity(key);
149 }
150 }
151
152 pub fn changed_field_names(&self, key: &EntityKey) -> BTreeSet<String> {
155 let mut fields = BTreeSet::new();
156 for change_set in &self.stack {
157 fields.extend(change_set.field_names(key));
158 }
159 fields
160 }
161}
162
163#[derive(Debug, Default)]
164pub struct RootContext {
165 change_sets: ChangeSetStack,
166 comment: Option<String>,
168 deleted_keys: std::collections::BTreeSet<EntityKey>,
171 new_keys: std::collections::BTreeSet<EntityKey>,
173 original_record: Option<Record>,
175 trace_chains: std::collections::BTreeMap<EntityKey, Vec<teaql_core::TraceNode>>,
177 original_versions: std::collections::BTreeMap<EntityKey, i64>,
179 is_new: bool,
181}
182
183#[derive(Debug, Clone, Default)]
184pub struct EntityRoot {
185 inner: Arc<Mutex<RootContext>>,
186}
187
188impl PartialEq for EntityRoot {
189 fn eq(&self, other: &Self) -> bool {
190 Arc::ptr_eq(&self.inner, &other.inner)
191 }
192}
193
194impl EntityRoot {
195 pub fn push_change_set(&self) {
196 self.inner
197 .lock()
198 .unwrap_or_else(|e| e.into_inner())
199 .change_sets
200 .push();
201 }
202
203 pub fn pop_change_set(&self) -> Option<EntityChangeSet> {
204 self.inner
205 .lock()
206 .unwrap_or_else(|e| e.into_inner())
207 .change_sets
208 .pop()
209 }
210
211 pub fn clear_current_change_set(&self) {
212 self.inner
213 .lock()
214 .unwrap_or_else(|e| e.into_inner())
215 .change_sets
216 .clear_current();
217 }
218
219 pub fn clear_committed(&self) {
222 let mut context = self.inner.lock().unwrap_or_else(|e| e.into_inner());
223 context.change_sets = ChangeSetStack::default();
224 context.deleted_keys.clear();
225 context.new_keys.clear();
226 context.original_versions.clear();
227 context.trace_chains.clear();
228 context.original_record = None;
229 context.comment = None;
230 context.is_new = false;
231 }
232
233 pub fn set(&self, key: EntityKey, field: impl Into<String>, value: impl Into<Value>) {
234 self.inner
235 .lock()
236 .unwrap_or_else(|e| e.into_inner())
237 .change_sets
238 .set(key, field, value.into());
239 }
240
241 pub fn get(&self, key: &EntityKey, field: &str) -> Option<Value> {
242 self.inner
243 .lock()
244 .unwrap_or_else(|e| e.into_inner())
245 .change_sets
246 .get(key, field)
247 }
248
249 pub fn current_change_set(&self) -> EntityChangeSet {
250 self.inner
251 .lock()
252 .unwrap_or_else(|e| e.into_inner())
253 .change_sets
254 .current()
255 .cloned()
256 .unwrap_or_default()
257 }
258
259 pub fn set_comment(&self, comment: impl Into<String>) {
262 self.inner.lock().unwrap_or_else(|e| e.into_inner()).comment = Some(comment.into());
263 }
264
265 pub fn get_comment(&self) -> Option<String> {
267 self.inner
268 .lock()
269 .unwrap_or_else(|e| e.into_inner())
270 .comment
271 .clone()
272 }
273
274 pub fn mark_as_new(&self, key: EntityKey) {
276 self.inner
277 .lock()
278 .unwrap_or_else(|e| e.into_inner())
279 .new_keys
280 .insert(key);
281 }
282
283 pub fn is_new(&self, key: &EntityKey) -> bool {
285 self.inner
286 .lock()
287 .unwrap_or_else(|e| e.into_inner())
288 .new_keys
289 .contains(key)
290 }
291
292 pub fn set_original_record(&self, record: Record) {
294 self.inner
295 .lock()
296 .unwrap_or_else(|e| e.into_inner())
297 .original_record = Some(record);
298 }
299
300 pub fn original_record(&self) -> Option<Record> {
302 self.inner
303 .lock()
304 .unwrap_or_else(|e| e.into_inner())
305 .original_record
306 .clone()
307 }
308
309 pub fn mark_as_delete(&self, key: EntityKey) {
314 let mut context = self.inner.lock().unwrap_or_else(|e| e.into_inner());
315 context.change_sets.clear_entity(&key);
316 context.deleted_keys.insert(key);
317 }
318
319 pub fn is_marked_as_delete(&self, key: &EntityKey) -> bool {
321 self.inner
322 .lock()
323 .unwrap_or_else(|e| e.into_inner())
324 .deleted_keys
325 .contains(key)
326 }
327
328 pub fn changed_field_names(&self, key: &EntityKey) -> BTreeSet<String> {
331 self.inner
332 .lock()
333 .unwrap_or_else(|e| e.into_inner())
334 .change_sets
335 .changed_field_names(key)
336 }
337 pub fn deleted_keys(&self) -> std::collections::BTreeSet<EntityKey> {
338 self.inner
339 .lock()
340 .unwrap_or_else(|e| e.into_inner())
341 .deleted_keys
342 .clone()
343 }
344
345 pub fn new_keys(&self) -> std::collections::BTreeSet<EntityKey> {
346 self.inner
347 .lock()
348 .unwrap_or_else(|e| e.into_inner())
349 .new_keys
350 .clone()
351 }
352
353 pub fn get_original_version(&self, key: &EntityKey) -> Option<i64> {
354 self.inner
355 .lock()
356 .unwrap_or_else(|e| e.into_inner())
357 .original_versions
358 .get(key)
359 .cloned()
360 }
361
362 pub fn get_trace_chain(&self, key: &EntityKey) -> Vec<teaql_core::TraceNode> {
363 self.inner
364 .lock()
365 .unwrap_or_else(|e| e.into_inner())
366 .trace_chains
367 .get(key)
368 .cloned()
369 .unwrap_or_default()
370 }
371
372 pub fn set_original_version(&self, key: EntityKey, version: i64) {
373 self.inner
374 .lock()
375 .unwrap_or_else(|e| e.into_inner())
376 .original_versions
377 .insert(key, version);
378 }
379}
380
381pub trait LedgerEntity: teaql_core::Entity {
382 fn entity_root(&self) -> Option<EntityRoot>;
383}