1use crate::value::Value;
4use rkyv::{Archive, Deserialize, Serialize};
5use serde::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize};
6
7#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize, SerdeSerialize, SerdeDeserialize)]
9pub enum Mutation {
10 Insert {
12 entity: String,
14 data: Vec<FieldValue>,
16 },
17 Update {
19 entity: String,
21 id: [u8; 16],
23 data: Vec<FieldValue>,
25 },
26 Delete {
28 entity: String,
30 id: [u8; 16],
32 },
33 Upsert {
35 entity: String,
37 id: Option<[u8; 16]>,
39 data: Vec<FieldValue>,
41 },
42}
43
44#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize, SerdeSerialize, SerdeDeserialize)]
46pub struct FieldValue {
47 pub field: String,
49 pub value: Value,
51}
52
53impl FieldValue {
54 pub fn new(field: impl Into<String>, value: impl Into<Value>) -> Self {
56 Self {
57 field: field.into(),
58 value: value.into(),
59 }
60 }
61}
62
63impl Mutation {
64 pub fn insert(entity: impl Into<String>, data: Vec<FieldValue>) -> Self {
66 Mutation::Insert {
67 entity: entity.into(),
68 data,
69 }
70 }
71
72 pub fn update(entity: impl Into<String>, id: [u8; 16], data: Vec<FieldValue>) -> Self {
74 Mutation::Update {
75 entity: entity.into(),
76 id,
77 data,
78 }
79 }
80
81 pub fn delete(entity: impl Into<String>, id: [u8; 16]) -> Self {
83 Mutation::Delete {
84 entity: entity.into(),
85 id,
86 }
87 }
88
89 pub fn upsert(entity: impl Into<String>, id: Option<[u8; 16]>, data: Vec<FieldValue>) -> Self {
91 Mutation::Upsert {
92 entity: entity.into(),
93 id,
94 data,
95 }
96 }
97
98 pub fn entity(&self) -> &str {
100 match self {
101 Mutation::Insert { entity, .. } => entity,
102 Mutation::Update { entity, .. } => entity,
103 Mutation::Delete { entity, .. } => entity,
104 Mutation::Upsert { entity, .. } => entity,
105 }
106 }
107}
108
109#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize, SerdeSerialize, SerdeDeserialize)]
111pub struct MutationBatch {
112 pub mutations: Vec<Mutation>,
114}
115
116impl MutationBatch {
117 pub fn new() -> Self {
119 Self { mutations: vec![] }
120 }
121
122 pub fn from_mutations(mutations: Vec<Mutation>) -> Self {
124 Self { mutations }
125 }
126
127 pub fn push(&mut self, mutation: Mutation) {
129 self.mutations.push(mutation);
130 }
131
132 pub fn is_empty(&self) -> bool {
134 self.mutations.is_empty()
135 }
136
137 pub fn len(&self) -> usize {
139 self.mutations.len()
140 }
141}
142
143impl Default for MutationBatch {
144 fn default() -> Self {
145 Self::new()
146 }
147}
148
149impl FromIterator<Mutation> for MutationBatch {
150 fn from_iter<T: IntoIterator<Item = Mutation>>(iter: T) -> Self {
151 Self {
152 mutations: iter.into_iter().collect(),
153 }
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn test_insert_mutation() {
163 let mutation = Mutation::insert(
164 "User",
165 vec![
166 FieldValue::new("name", "Alice"),
167 FieldValue::new("email", "alice@example.com"),
168 FieldValue::new("active", true),
169 ],
170 );
171
172 if let Mutation::Insert { entity, data } = &mutation {
173 assert_eq!(entity, "User");
174 assert_eq!(data.len(), 3);
175 assert_eq!(data[0].field, "name");
176 } else {
177 panic!("Expected Insert mutation");
178 }
179 }
180
181 #[test]
182 fn test_update_mutation() {
183 let id = [1u8; 16];
184 let mutation = Mutation::update(
185 "User",
186 id,
187 vec![FieldValue::new("name", "Bob"), FieldValue::new("age", 30i32)],
188 );
189
190 if let Mutation::Update {
191 entity,
192 id: update_id,
193 data,
194 } = &mutation
195 {
196 assert_eq!(entity, "User");
197 assert_eq!(*update_id, id);
198 assert_eq!(data.len(), 2);
199 } else {
200 panic!("Expected Update mutation");
201 }
202 }
203
204 #[test]
205 fn test_delete_mutation() {
206 let id = [2u8; 16];
207 let mutation = Mutation::delete("User", id);
208
209 if let Mutation::Delete {
210 entity,
211 id: delete_id,
212 } = &mutation
213 {
214 assert_eq!(entity, "User");
215 assert_eq!(*delete_id, id);
216 } else {
217 panic!("Expected Delete mutation");
218 }
219 }
220
221 #[test]
222 fn test_mutation_batch() {
223 let mut batch = MutationBatch::new();
224 assert!(batch.is_empty());
225
226 batch.push(Mutation::insert(
227 "User",
228 vec![FieldValue::new("name", "Test")],
229 ));
230 batch.push(Mutation::delete("Session", [0u8; 16]));
231
232 assert_eq!(batch.len(), 2);
233 assert!(!batch.is_empty());
234 }
235
236 #[test]
237 fn test_mutation_serialization_roundtrip() {
238 let mutations = vec![
239 Mutation::insert(
240 "Post",
241 vec![
242 FieldValue::new("title", "Hello World"),
243 FieldValue::new("published", false),
244 ],
245 ),
246 Mutation::update(
247 "Post",
248 [1u8; 16],
249 vec![FieldValue::new("published", true)],
250 ),
251 Mutation::delete("Comment", [2u8; 16]),
252 ];
253
254 let batch = MutationBatch::from_mutations(mutations);
255
256 let bytes = rkyv::to_bytes::<rkyv::rancor::Error>(&batch).unwrap();
257 let archived = rkyv::access::<ArchivedMutationBatch, rkyv::rancor::Error>(&bytes).unwrap();
258 let deserialized: MutationBatch =
259 rkyv::deserialize::<MutationBatch, rkyv::rancor::Error>(archived).unwrap();
260
261 assert_eq!(batch, deserialized);
262 }
263}