rust_query/mutable.rs
1use std::{
2 any::Any,
3 ops::{Deref, DerefMut},
4};
5
6use crate::{
7 IntoExpr, Table, TableRow, Transaction, scoped_transaction::MutTemp,
8 transaction::try_update_private,
9};
10
11/// [Mutable] access to columns of a single table row.
12///
13/// The whole row is retrieved and can be inspected/updated from Rust code.
14///
15/// Only rows that are not used in a `#[unique]` constraint can be updated directly by dereferencing [Mutable].
16/// To update columns with a unique constraint, you have to use [Mutable::unique].
17pub struct Mutable<'transaction, T: Table> {
18 pub(crate) temp: &'transaction mut MutTemp<T>,
19}
20
21impl<'transaction, T: Table> Mutable<'transaction, T> {
22 pub(crate) fn new(temp: &'transaction mut dyn Any) -> Self {
23 Self {
24 temp: temp.downcast_mut().unwrap(),
25 }
26 }
27
28 /// Turn the [Mutable] into a [TableRow].
29 ///
30 /// This will end the lifetime of the [Mutable], which is useful since
31 /// [Mutable] does not have a non lexical lifetime, because of the [Drop] impl.
32 ///
33 /// If you do not need the [TableRow], then it is also possible to just call [drop].
34 pub fn table_row(&self) -> TableRow<T> {
35 self.temp.row_id
36 }
37
38 /// Update unique constraint columns.
39 ///
40 /// When the update succeeds, this function returns [Ok], when it fails it returns [Err] with one of
41 /// three conflict types:
42 /// - 0 unique constraints => [std::convert::Infallible]
43 /// - 1 unique constraint => [TableRow] reference to the conflicting table row.
44 /// - 2+ unique constraints => [crate::Conflict]
45 ///
46 /// If any of the changes made inside the closure conflict with an existing row, then all changes
47 /// made inside the closure are reverted.
48 ///
49 /// If the closure panics, then all changes made inside the closure are also reverted.
50 /// Applying those changes is not possible, as conflicts can not be reported if there is a panic.
51 pub fn unique<O>(
52 &mut self,
53 f: impl FnOnce(&mut <T::Mutable as Deref>::Target) -> O,
54 ) -> Result<O, T::Conflict> {
55 // taking the data puts it in a guaranteed valid state
56 if let Some(update) = self.temp.inner.take() {
57 try_update_private(self.temp.row_id, update)
58 .expect("flushing non unique update should always work");
59 }
60
61 let data = Transaction::new_ref().query_one(T::into_select(self.temp.row_id.into_expr()));
62 let mut data = T::select_mutable(data);
63
64 // no need to catch panics here because we already guaranteed a valid state.
65 let out = f(T::mutable_as_unique(&mut data));
66
67 // only apply the update if there was no panic
68 try_update_private(self.temp.row_id, data)?;
69
70 Ok(out)
71 }
72}
73
74impl<'transaction, T: Table> Deref for Mutable<'transaction, T> {
75 type Target = T::Mutable;
76
77 fn deref(&self) -> &Self::Target {
78 self.temp.inner.get_or_init(|| {
79 let data =
80 Transaction::new_ref().query_one(T::into_select(self.temp.row_id.into_expr()));
81 T::select_mutable(data)
82 })
83 }
84}
85
86impl<'transaction, T: Table> DerefMut for Mutable<'transaction, T> {
87 fn deref_mut(&mut self) -> &mut Self::Target {
88 let _ = Deref::deref(self);
89 self.temp.inner.get_mut().unwrap()
90 }
91}