Skip to main content

co_primitives/types/
lazy_transaction.rs

1// SPDX-License-Identifier: AGPL-3.0-only
2// Copyright (C) 2026 1io BRANDGUARDIAN GmbH
3
4use crate::{BlockStorage, StorageError};
5use async_trait::async_trait;
6use either::Either;
7
8/// Collection which supports transactions.
9#[async_trait]
10pub trait Transactionable<S>
11where
12	S: BlockStorage + Clone + 'static,
13{
14	type Transaction;
15
16	async fn open(&self, storage: &S) -> Result<Self::Transaction, StorageError>;
17}
18
19/// Lazy transaction that only opens the transaction when used.
20#[derive(Debug)]
21pub struct LazyTransaction<S, T>(Either<(S, T), (T::Transaction, bool)>)
22where
23	S: BlockStorage + Clone + 'static,
24	T: Transactionable<S> + 'static;
25impl<S, T> LazyTransaction<S, T>
26where
27	S: BlockStorage + Clone + 'static,
28	T: Transactionable<S> + 'static,
29{
30	pub fn new(storage: S, init: T) -> Self {
31		Self(Either::Left((storage, init)))
32	}
33
34	/// Whether this transaction has been accessed mutable yet.
35	pub fn is_mut_access(&self) -> bool {
36		match &self.0 {
37			Either::Left(_) => false,
38			Either::Right((_, is_mut)) => *is_mut,
39		}
40	}
41
42	async fn open(&mut self) -> Result<(), StorageError> {
43		match &self.0 {
44			Either::Left((storage, item)) => {
45				self.0 = Either::Right((item.open(storage).await?, false));
46			},
47			Either::Right(_) => {},
48		}
49		Ok(())
50	}
51
52	pub async fn get(&mut self) -> Result<&T::Transaction, StorageError> {
53		self.open().await?;
54		Ok(self.opt().expect("initialized after open"))
55	}
56
57	pub async fn get_mut(&mut self) -> Result<&mut T::Transaction, StorageError> {
58		self.open().await?;
59		Ok(self.opt_mut().expect("initialized after open"))
60	}
61
62	pub fn opt(&self) -> Option<&T::Transaction> {
63		match &self.0 {
64			Either::Left(_) => None,
65			Either::Right((transaction, _is_mut_access)) => Some(transaction),
66		}
67	}
68
69	pub fn opt_mut(&mut self) -> Option<&mut T::Transaction> {
70		match &mut self.0 {
71			Either::Left(_) => None,
72			Either::Right((transaction, is_mut_access)) => {
73				*is_mut_access = true;
74				Some(transaction)
75			},
76		}
77	}
78
79	pub fn opt_if_is_mut_access(&mut self) -> Option<&mut T::Transaction> {
80		match &mut self.0 {
81			Either::Right((transaction, true)) => Some(transaction),
82			_ => None,
83		}
84	}
85}