Skip to main content

teaql_core/
mutation.rs

1use std::collections::BTreeMap;
2use std::ops::{Deref, DerefMut};
3
4use crate::Value;
5
6/// Field values intentionally supplied to a database mutation.
7///
8/// This is distinct from a loaded entity snapshot and from provider-generated
9/// values. Keeping the types separate prevents a generic row map from becoming
10/// the runtime's mutation model.
11#[derive(Debug, Clone, Default, PartialEq)]
12pub struct MutationValues(BTreeMap<String, Value>);
13
14impl MutationValues {
15    pub fn new() -> Self {
16        Self::default()
17    }
18}
19
20impl Deref for MutationValues {
21    type Target = BTreeMap<String, Value>;
22
23    fn deref(&self) -> &Self::Target {
24        &self.0
25    }
26}
27
28impl DerefMut for MutationValues {
29    fn deref_mut(&mut self) -> &mut Self::Target {
30        &mut self.0
31    }
32}
33
34impl From<BTreeMap<String, Value>> for MutationValues {
35    fn from(values: BTreeMap<String, Value>) -> Self {
36        Self(values)
37    }
38}
39
40impl From<MutationValues> for BTreeMap<String, Value> {
41    fn from(values: MutationValues) -> Self {
42        values.0
43    }
44}
45
46impl IntoIterator for MutationValues {
47    type Item = (String, Value);
48    type IntoIter = std::collections::btree_map::IntoIter<String, Value>;
49
50    fn into_iter(self) -> Self::IntoIter {
51        self.0.into_iter()
52    }
53}
54
55impl<'a> IntoIterator for &'a MutationValues {
56    type Item = (&'a String, &'a Value);
57    type IntoIter = std::collections::btree_map::Iter<'a, String, Value>;
58
59    fn into_iter(self) -> Self::IntoIter {
60        self.0.iter()
61    }
62}
63
64/// Values assigned by a persistence provider, such as generated identifiers
65/// or database defaults. They are provider output, not mutation input.
66#[derive(Debug, Clone, Default, PartialEq)]
67pub struct GeneratedValues(BTreeMap<String, Value>);
68
69impl GeneratedValues {
70    pub fn new() -> Self {
71        Self::default()
72    }
73}
74
75impl Deref for GeneratedValues {
76    type Target = BTreeMap<String, Value>;
77
78    fn deref(&self) -> &Self::Target {
79        &self.0
80    }
81}
82
83impl DerefMut for GeneratedValues {
84    fn deref_mut(&mut self) -> &mut Self::Target {
85        &mut self.0
86    }
87}
88
89impl From<BTreeMap<String, Value>> for GeneratedValues {
90    fn from(values: BTreeMap<String, Value>) -> Self {
91        Self(values)
92    }
93}
94
95impl From<GeneratedValues> for BTreeMap<String, Value> {
96    fn from(values: GeneratedValues) -> Self {
97        values.0
98    }
99}
100
101/// Previously persisted field values used for optimistic checks and audit
102/// comparison. It cannot be passed where new mutation values are expected.
103#[derive(Debug, Clone, Default, PartialEq)]
104pub struct EntitySnapshot(BTreeMap<String, Value>);
105
106impl EntitySnapshot {
107    pub fn new() -> Self {
108        Self::default()
109    }
110}
111
112impl Deref for EntitySnapshot {
113    type Target = BTreeMap<String, Value>;
114
115    fn deref(&self) -> &Self::Target {
116        &self.0
117    }
118}
119
120impl DerefMut for EntitySnapshot {
121    fn deref_mut(&mut self) -> &mut Self::Target {
122        &mut self.0
123    }
124}
125
126impl From<BTreeMap<String, Value>> for EntitySnapshot {
127    fn from(values: BTreeMap<String, Value>) -> Self {
128        Self(values)
129    }
130}
131
132impl From<EntitySnapshot> for BTreeMap<String, Value> {
133    fn from(values: EntitySnapshot) -> Self {
134        values.0
135    }
136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq)]
139pub enum MutationKind {
140    Insert,
141    Update,
142    Delete,
143    Recover,
144}
145
146#[derive(Debug, Clone, PartialEq)]
147pub struct InsertCommand {
148    pub entity: String,
149    pub values: MutationValues,
150    pub trace_chain: Vec<crate::TraceNode>,
151}
152
153impl InsertCommand {
154    pub fn new(entity: impl Into<String>) -> Self {
155        Self {
156            entity: entity.into(),
157            values: MutationValues::new(),
158            trace_chain: Vec::new(),
159        }
160    }
161
162    pub fn value(mut self, field: impl Into<String>, value: impl Into<Value>) -> Self {
163        self.values.insert(field.into(), value.into());
164        self
165    }
166}
167
168#[derive(Debug, Clone, PartialEq)]
169pub struct UpdateCommand {
170    pub entity: String,
171    pub id: Value,
172    pub expected_version: Option<i64>,
173    pub values: MutationValues,
174    pub trace_chain: Vec<crate::TraceNode>,
175    pub old_values: Option<EntitySnapshot>,
176}
177
178impl UpdateCommand {
179    pub fn new(entity: impl Into<String>, id: impl Into<Value>) -> Self {
180        Self {
181            entity: entity.into(),
182            id: id.into(),
183            expected_version: None,
184            values: MutationValues::new(),
185            trace_chain: Vec::new(),
186            old_values: None,
187        }
188    }
189
190    pub fn expected_version(mut self, version: i64) -> Self {
191        self.expected_version = Some(version);
192        self
193    }
194
195    pub fn value(mut self, field: impl Into<String>, value: impl Into<Value>) -> Self {
196        self.values.insert(field.into(), value.into());
197        self
198    }
199}
200
201#[derive(Debug, Clone, PartialEq)]
202pub struct BatchInsertCommand {
203    pub entity: String,
204    pub batch_values: Vec<MutationValues>,
205    pub trace_chains: Vec<Vec<crate::TraceNode>>,
206}
207
208impl BatchInsertCommand {
209    pub fn new(entity: impl Into<String>) -> Self {
210        Self {
211            entity: entity.into(),
212            batch_values: Vec::new(),
213            trace_chains: Vec::new(),
214        }
215    }
216}
217
218#[derive(Debug, Clone, PartialEq)]
219pub struct BatchUpdateCommand {
220    pub entity: String,
221    pub batch_ids: Vec<Value>,
222    pub batch_expected_versions: Vec<Option<i64>>,
223    pub batch_values: Vec<MutationValues>,
224    pub update_fields: Vec<String>,
225    pub trace_chains: Vec<Vec<crate::TraceNode>>,
226    pub batch_old_values: Vec<Option<EntitySnapshot>>,
227}
228
229impl BatchUpdateCommand {
230    pub fn new(entity: impl Into<String>, update_fields: Vec<String>) -> Self {
231        Self {
232            entity: entity.into(),
233            batch_ids: Vec::new(),
234            batch_expected_versions: Vec::new(),
235            batch_values: Vec::new(),
236            update_fields,
237            trace_chains: Vec::new(),
238            batch_old_values: Vec::new(),
239        }
240    }
241}
242
243#[derive(Debug, Clone, PartialEq)]
244pub struct DeleteCommand {
245    pub entity: String,
246    pub id: Value,
247    pub expected_version: Option<i64>,
248    pub soft_delete: bool,
249    pub trace_chain: Vec<crate::TraceNode>,
250}
251
252impl DeleteCommand {
253    pub fn new(entity: impl Into<String>, id: impl Into<Value>) -> Self {
254        Self {
255            entity: entity.into(),
256            id: id.into(),
257            expected_version: None,
258            soft_delete: true,
259            trace_chain: Vec::new(),
260        }
261    }
262
263    pub fn expected_version(mut self, version: i64) -> Self {
264        self.expected_version = Some(version);
265        self
266    }
267
268    pub fn hard_delete(mut self) -> Self {
269        self.soft_delete = false;
270        self
271    }
272}
273
274#[derive(Debug, Clone, PartialEq)]
275pub struct RecoverCommand {
276    pub entity: String,
277    pub id: Value,
278    pub expected_version: i64,
279    pub trace_chain: Vec<crate::TraceNode>,
280}
281
282impl RecoverCommand {
283    pub fn new(entity: impl Into<String>, id: impl Into<Value>, expected_version: i64) -> Self {
284        Self {
285            entity: entity.into(),
286            id: id.into(),
287            expected_version,
288            trace_chain: Vec::new(),
289        }
290    }
291}