1use alloc::vec;
4use alloc::vec::Vec;
5use core::fmt::Debug;
6
7use crate::{
8 DynTable, SchemaWithPK,
9 builders::{ChangesetFormat, PatchsetFormat, format::Format, operation::Indirect},
10 encoding::{MaybeValue, Value},
11};
12
13#[derive(Debug, Clone)]
14pub struct Update<T, F: Format<S, B>, S, B> {
16 table: T,
18 pub(super) values: Vec<(F::Old, MaybeValue<S, B>)>,
21 pub(crate) indirect: bool,
23}
24
25impl<
26 T: DynTable + PartialEq,
27 F: Format<S, B>,
28 S: PartialEq + AsRef<str>,
29 B: PartialEq + AsRef<[u8]>,
30> PartialEq for Update<T, F, S, B>
31where
32 F::Old: PartialEq,
33{
34 fn eq(&self, other: &Self) -> bool {
35 self.table == other.table && self.values == other.values && self.indirect == other.indirect
36 }
37}
38
39impl<T: DynTable + Eq, F: Format<S, B>, S: Eq + AsRef<str>, B: Eq + AsRef<[u8]>> Eq
40 for Update<T, F, S, B>
41where
42 F::Old: Eq,
43{
44}
45
46impl<T: DynTable, F: Format<S, B>, S: AsRef<str>, B: AsRef<[u8]>> From<Update<T, F, S, B>>
47 for Vec<(F::Old, MaybeValue<S, B>)>
48{
49 #[inline]
50 fn from(update: Update<T, F, S, B>) -> Self {
51 update.values
52 }
53}
54
55impl<T, F: Format<S, B>, S, B> AsRef<T> for Update<T, F, S, B> {
56 #[inline]
57 fn as_ref(&self) -> &T {
58 &self.table
59 }
60}
61
62impl<T, F: Format<S, B>, S: Clone, B: Clone> Update<T, F, S, B> {
63 #[inline]
65 pub fn values(&self) -> &[(F::Old, MaybeValue<S, B>)] {
66 &self.values
67 }
68
69 #[inline]
70 pub fn extract_pk(&self) -> Vec<Value<S, B>>
72 where
73 T: SchemaWithPK,
74 {
75 self.table.extract_pk(&self.values)
76 }
77}
78
79impl<T: DynTable, F: Format<S, B>, S: Clone + AsRef<str>, B: Clone + AsRef<[u8]>> From<T>
80 for Update<T, F, S, B>
81where
82 F::Old: Clone,
83{
84 #[inline]
85 fn from(table: T) -> Self {
86 let num_cols = table.number_of_columns();
87 Self {
88 table,
89 values: vec![(F::Old::default(), None); num_cols],
90 indirect: false,
91 }
92 }
93}
94
95impl<T, F: Format<S, B>, S, B> Indirect for Update<T, F, S, B> {
96 #[inline]
97 fn indirect(mut self, indirect: bool) -> Self {
98 self.indirect = indirect;
99 self
100 }
101}
102
103impl<T: DynTable, S: Clone + Debug + AsRef<str>, B: Clone + Debug + AsRef<[u8]>>
104 Update<T, ChangesetFormat, S, B>
105{
106 pub fn set(
119 mut self,
120 col_idx: usize,
121 old: impl Into<Value<S, B>>,
122 new: impl Into<Value<S, B>>,
123 ) -> Result<Self, crate::errors::Error> {
124 if col_idx >= self.values.len() {
125 return Err(crate::errors::Error::ColumnIndexOutOfBounds(
126 col_idx,
127 self.values.len(),
128 ));
129 }
130
131 self.values[col_idx] = (Some(old.into()), Some(new.into()));
132 Ok(self)
133 }
134
135 pub fn set_new(
164 mut self,
165 col_idx: usize,
166 new: impl Into<Value<S, B>>,
167 ) -> Result<Self, crate::errors::Error> {
168 if col_idx >= self.values.len() {
169 return Err(crate::errors::Error::ColumnIndexOutOfBounds(
170 col_idx,
171 self.values.len(),
172 ));
173 }
174
175 self.values[col_idx] = (None, Some(new.into()));
176 Ok(self)
177 }
178
179 #[inline]
199 pub fn set_null(self, col_idx: usize) -> Result<Self, crate::errors::Error>
200 where
201 S: Default,
202 B: Default,
203 {
204 self.set(col_idx, Value::Null, Value::Null)
205 }
206}
207
208impl<T: DynTable, S: AsRef<str>, B: AsRef<[u8]>> Update<T, PatchsetFormat, S, B> {
209 pub fn set(
226 mut self,
227 col_idx: usize,
228 new: impl Into<Value<S, B>>,
229 ) -> Result<Self, crate::errors::Error> {
230 if col_idx >= self.values.len() {
231 return Err(crate::errors::Error::ColumnIndexOutOfBounds(
232 col_idx,
233 self.values.len(),
234 ));
235 }
236
237 self.values[col_idx] = ((), Some(new.into()));
238 Ok(self)
239 }
240
241 pub fn set_null(self, col_idx: usize) -> Result<Self, crate::errors::Error>
261 where
262 S: Default,
263 B: Default,
264 {
265 self.set(col_idx, Value::Null)
266 }
267}
268
269#[cfg(test)]
270mod tests {
271 use super::Update;
272 use crate::builders::{ChangesetFormat, PatchsetFormat};
273 use crate::errors::Error;
274 use crate::schema::SimpleTable;
275 use alloc::string::String;
276 use alloc::vec::Vec;
277
278 fn users() -> SimpleTable {
279 SimpleTable::new("users", &["id", "name"], &[0])
280 }
281
282 #[test]
283 fn test_changeset_update_set_out_of_bounds() {
284 let err = Update::<_, ChangesetFormat, String, Vec<u8>>::from(users())
285 .set(7, 1i64, 2i64)
286 .unwrap_err();
287 assert!(
288 matches!(err, Error::ColumnIndexOutOfBounds(7, 2)),
289 "got {err:?}"
290 );
291 }
292
293 #[test]
294 fn test_changeset_update_set_new_out_of_bounds() {
295 let err = Update::<_, ChangesetFormat, String, Vec<u8>>::from(users())
296 .set_new(7, "x")
297 .unwrap_err();
298 assert!(
299 matches!(err, Error::ColumnIndexOutOfBounds(7, 2)),
300 "got {err:?}"
301 );
302 }
303
304 #[test]
305 fn test_changeset_update_set_null_out_of_bounds() {
306 let err = Update::<_, ChangesetFormat, String, Vec<u8>>::from(users())
307 .set_null(2)
308 .unwrap_err();
309 assert!(
310 matches!(err, Error::ColumnIndexOutOfBounds(2, 2)),
311 "got {err:?}"
312 );
313 }
314
315 #[test]
316 fn test_patchset_update_set_out_of_bounds() {
317 let err = Update::<_, PatchsetFormat, String, Vec<u8>>::from(users())
318 .set(3, 1i64)
319 .unwrap_err();
320 assert!(
321 matches!(err, Error::ColumnIndexOutOfBounds(3, 2)),
322 "got {err:?}"
323 );
324 }
325
326 #[test]
327 fn test_patchset_update_set_null_out_of_bounds() {
328 let err = Update::<_, PatchsetFormat, String, Vec<u8>>::from(users())
329 .set_null(5)
330 .unwrap_err();
331 assert!(
332 matches!(err, Error::ColumnIndexOutOfBounds(5, 2)),
333 "got {err:?}"
334 );
335 }
336
337 #[test]
338 fn test_update_eq() {
339 let a = Update::<_, ChangesetFormat, String, Vec<u8>>::from(users())
340 .set(0, 1i64, 2i64)
341 .unwrap();
342 let b = Update::<_, ChangesetFormat, String, Vec<u8>>::from(users())
343 .set(0, 1i64, 2i64)
344 .unwrap();
345 assert_eq!(a, b);
346
347 let c = Update::<_, ChangesetFormat, String, Vec<u8>>::from(users())
348 .set(0, 1i64, 3i64)
349 .unwrap();
350 assert_ne!(a, c);
351 }
352}