repository/
prealloc_tx.rs1use crate::{EntityPtr, Repo};
15use alloc::collections::BTreeSet;
16use core::any::Any;
17use core::fmt;
18use thiserror::Error;
19
20pub struct PreallocTx<'a> {
26 repo: &'a mut Repo,
27 preallocations: BTreeSet<crate::EntityRecord>,
28}
29
30impl<'a> fmt::Debug for PreallocTx<'a> {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 write!(f, "PreAllocTx {{..}}")
33 }
34}
35
36#[derive(Error)]
37pub enum PreallocTxError<T: Any, R = Repo> {
39 #[error("invalid preallocation entity pointer")]
40 InvalidPreallocPtr(EntityPtr<T, R>),
42 #[error("invalid preallocation entity pointer with value")]
43 InvalidPreallocPtrWithValue(EntityPtr<T, R>, T),
45 #[error("preallocation is already occupied")]
46 PreallocOccupied(EntityPtr<T, R>),
48 #[error("internal error")]
49 RepoError(#[from] crate::Error),
51}
52
53impl<T: Any, R> fmt::Debug for PreallocTxError<T, R> {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 match self {
56 PreallocTxError::InvalidPreallocPtr(p) => write!(f, "InvalidPreallocPtr({:?})", p),
57 PreallocTxError::InvalidPreallocPtrWithValue(p, _) => {
58 write!(f, "InvalidPreallocPtr({:?}, <value>)", p)
59 }
60 PreallocTxError::PreallocOccupied(p) => write!(f, "PreallocOccupied({:?})", p),
61 PreallocTxError::RepoError(e) => write!(f, "RepoError({:?})", e),
62 }
63 }
64}
65
66impl<'a> PreallocTx<'a> {
67 pub(crate) fn new(repo: &'a mut Repo) -> Self {
68 PreallocTx {
69 repo,
70 preallocations: Default::default(),
71 }
72 }
73
74 pub fn repo(&self) -> &Repo {
76 self.repo
77 }
78
79 pub fn repo_mut(&mut self) -> &mut Repo {
81 self.repo
82 }
83
84 pub fn preallocate<T: Any>(&mut self) -> EntityPtr<T> {
86 let v = self.repo.preallocate_entity::<T>();
87 assert!(
88 !self.preallocations.contains(&v.record),
89 "unusable preallocation retrieved"
90 );
91 self.preallocations.insert(v.record);
92 v
93 }
94
95 pub fn init_preallocation<T: Any, R>(
97 &mut self,
98 ptr: EntityPtr<T, R>,
99 value: T,
100 ) -> Result<(), PreallocTxError<T, R>> {
101 if let Some(_) = self.preallocations.take(&ptr.record) {
102 if !self.repo.validate_record_type::<T>(ptr.record) {
103 return Err(PreallocTxError::InvalidPreallocPtr(ptr));
104 }
105 if let Err((_, v)) = self.repo.init_preallocate_entity(ptr.record, value) {
106 return Err(PreallocTxError::InvalidPreallocPtrWithValue(ptr, v));
107 }
108 Ok(())
109 } else {
110 Err(PreallocTxError::InvalidPreallocPtrWithValue(ptr, value))
111 }
112 }
113
114 pub fn cancel_preallocation<T: Any, R>(
119 &mut self,
120 ptr: EntityPtr<T, R>,
121 ) -> Result<(), PreallocTxError<T, R>> {
122 if self.preallocations.contains(&ptr.record) {
123 if !self.repo.validate_record_type::<T>(ptr.record) {
124 return Err(PreallocTxError::InvalidPreallocPtr(ptr));
125 }
126 if !self.repo.cancel_preallocate_entity(ptr.record)? {
127 return Err(PreallocTxError::PreallocOccupied(ptr));
128 }
129 self.preallocations.remove(&ptr.record);
130 Ok(())
131 } else {
132 Err(PreallocTxError::InvalidPreallocPtr(ptr))
133 }
134 }
135}
136
137impl<'a> Drop for PreallocTx<'a> {
138 fn drop(&mut self) {
139 use core::mem;
140 let mut preallocations = Default::default();
141 mem::swap(&mut preallocations, &mut self.preallocations);
142 for record in preallocations {
143 let _ = self.repo.cancel_preallocate_entity(record);
144 }
145 }
146}
147
148impl Repo {
149 pub fn transaction_preallocate<TxFn, T, E>(&mut self, transaction: TxFn) -> Result<T, E>
153 where
154 TxFn: FnOnce(&mut PreallocTx<'_>) -> Result<T, E>,
155 {
156 let mut prealloc_tx = PreallocTx::new(self);
157 (transaction)(&mut prealloc_tx)
158 }
159}