1use std::{
2 cell::OnceCell,
3 marker::PhantomData,
4 ops::{Deref, DerefMut},
5 panic::AssertUnwindSafe,
6};
7
8use crate::{IntoExpr, Table, TableRow, Transaction};
9
10pub struct Mutable<'transaction, T: Table> {
18 cell: OnceCell<MutableInner<T>>,
19 row_id: TableRow<T>,
20 _txn: PhantomData<&'transaction mut Transaction<T::Schema>>,
21}
22
23struct MutableInner<T: Table> {
24 val: T::Mutable,
25 any_update: bool,
26}
27
28impl<T: Table> MutableInner<T> {
29 fn new(row_id: TableRow<T>) -> Self {
30 let select = Transaction::new_ref().query_one(T::into_select(row_id.into_expr()));
31 Self {
32 val: T::select_mutable(select),
33 any_update: false,
34 }
35 }
36}
37
38impl<'transaction, T: Table> Mutable<'transaction, T> {
39 pub(crate) fn new(inner: T::Mutable, row_id: TableRow<T>) -> Self {
40 Self {
41 cell: OnceCell::from(MutableInner {
42 val: inner,
43 any_update: false,
44 }),
45 row_id,
46 _txn: PhantomData,
47 }
48 }
49
50 pub fn into_table_row(self) -> TableRow<T> {
57 self.row_id
58 }
59
60 pub fn unique<O>(
74 &mut self,
75 f: impl FnOnce(&mut <T::Mutable as Deref>::Target) -> O,
76 ) -> Result<O, T::Conflict> {
77 *self = Mutable {
79 cell: OnceCell::new(),
80 row_id: self.row_id,
81 _txn: PhantomData,
82 };
83 let res = std::panic::catch_unwind(AssertUnwindSafe(|| f(T::mutable_as_unique(self))));
86 let update = self.cell.take().unwrap().val;
89 let out = match res {
90 Ok(out) => out,
91 Err(payload) => std::panic::resume_unwind(payload),
92 };
93 Transaction::new_ref().update(self.row_id, update)?;
95
96 Ok(out)
97 }
98}
99
100impl<'transaction, T: Table> Deref for Mutable<'transaction, T> {
101 type Target = T::Mutable;
102
103 fn deref(&self) -> &Self::Target {
104 &self.cell.get_or_init(|| MutableInner::new(self.row_id)).val
105 }
106}
107
108impl<'transaction, T: Table> DerefMut for Mutable<'transaction, T> {
109 fn deref_mut(&mut self) -> &mut Self::Target {
110 let _ = Mutable::deref(self);
112 let inner = self.cell.get_mut().unwrap();
113 inner.any_update = true;
114 &mut inner.val
115 }
116}
117
118impl<'transaction, T: Table> Drop for Mutable<'transaction, T> {
119 fn drop(&mut self) {
120 let Some(cell) = self.cell.take() else {
121 return;
122 };
123 if cell.any_update {
124 let update = cell.val;
125 let Ok(_) = Transaction::new_ref().update(self.row_id, update) else {
126 panic!("mutable can not fail, no unique is updated")
127 };
128 }
129 }
130}
131
132#[cfg(test)]
133mod tests {
134
135 use crate::{Database, migration::Config};
136
137 #[test]
138 fn mutable_shenanigans() {
139 #[crate::migration::schema(Test)]
140 pub mod vN {
141 pub struct Foo {
142 pub alpha: i64,
143 #[unique]
144 pub bravo: i64,
145 }
146 }
147 use v0::*;
148
149 let err = std::panic::catch_unwind(move || {
150 let db = Database::new(Config::open_in_memory());
151 db.transaction_mut_ok(|txn| {
152 txn.insert(Foo { alpha: 1, bravo: 1 }).unwrap();
153 let row = txn.insert(Foo { alpha: 1, bravo: 2 }).unwrap();
154 let mut mutable = txn.mutable(row);
155 mutable.alpha = 100;
156 mutable
157 .unique(|x| {
158 x.bravo = 1;
159 })
160 .unwrap_err();
161 assert_eq!(mutable.alpha, 100);
162 assert_eq!(mutable.bravo, 2);
163
164 let row = mutable.into_table_row();
165 let view = txn.lazy(row);
166 assert_eq!(view.alpha, 100);
167 assert_eq!(view.bravo, 2);
168
169 let mut mutable = txn.mutable(row);
170 mutable.alpha = 200;
171 mutable
172 .unique(|x| {
173 x.bravo = 1;
174 panic!("error in unique")
175 })
176 .unwrap();
177 });
178 })
179 .unwrap_err();
180 assert_eq!(*err.downcast_ref::<&str>().unwrap(), "error in unique");
181 }
182}