Skip to main content

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}