1use super::{Deletable, Gettable, Updatable};
4use crate::qmdb::{operation::Key, Error};
5use commonware_codec::CodecShared;
6use std::{collections::BTreeMap, future::Future};
7
8pub struct Batch<'a, K, V, D>
12where
13 K: Key,
14 V: CodecShared + Clone,
15 D: Gettable<Key = K, Value = V, Error = Error> + Sync,
16{
17 db: &'a D,
19 diff: BTreeMap<K, Option<V>>,
26}
27
28impl<'a, K, V, D> Batch<'a, K, V, D>
29where
30 K: Key,
31 V: CodecShared + Clone,
32 D: Gettable<Key = K, Value = V, Error = Error> + Sync,
33{
34 pub const fn new(db: &'a D) -> Self {
36 Self {
37 db,
38 diff: BTreeMap::new(),
39 }
40 }
41
42 pub fn delete_unchecked(&mut self, key: K) -> Result<(), Error> {
44 self.diff.insert(key, None);
45
46 Ok(())
47 }
48}
49
50impl<'a, K, V, D> Gettable for Batch<'a, K, V, D>
51where
52 K: Key,
53 V: CodecShared + Clone,
54 D: Gettable<Key = K, Value = V, Error = Error> + Sync,
55{
56 type Key = K;
57 type Value = V;
58 type Error = Error;
59
60 async fn get(&self, key: &K) -> Result<Option<V>, Error> {
63 if let Some(value) = self.diff.get(key) {
64 return Ok(value.clone());
65 }
66
67 self.db.get(key).await
68 }
69}
70
71impl<'a, K, V, D> Updatable for Batch<'a, K, V, D>
72where
73 K: Key,
74 V: CodecShared + Clone,
75 D: Gettable<Key = K, Value = V, Error = Error> + Sync,
76{
77 async fn update(&mut self, key: K, value: V) -> Result<(), Error> {
79 self.diff.insert(key, Some(value));
80
81 Ok(())
82 }
83}
84
85impl<'a, K, V, D> Deletable for Batch<'a, K, V, D>
86where
87 K: Key,
88 V: CodecShared + Clone,
89 D: Gettable<Key = K, Value = V, Error = Error> + Sync,
90{
91 async fn delete(&mut self, key: K) -> Result<bool, Error> {
94 if let Some(entry) = self.diff.get_mut(&key) {
95 match entry {
96 Some(_) => {
97 *entry = None;
98 return Ok(true);
99 }
100 None => return Ok(false),
101 }
102 }
103
104 if self.db.get(&key).await?.is_some() {
105 self.diff.insert(key, None);
106 return Ok(true);
107 }
108
109 Ok(false)
110 }
111}
112
113impl<'a, K, V, D> IntoIterator for Batch<'a, K, V, D>
114where
115 K: Key,
116 V: CodecShared + Clone,
117 D: Gettable<Key = K, Value = V, Error = Error> + Sync,
118{
119 type Item = (K, Option<V>);
120 type IntoIter = std::collections::btree_map::IntoIter<K, Option<V>>;
121
122 fn into_iter(self) -> Self::IntoIter {
123 self.diff.into_iter()
124 }
125}
126
127pub trait Batchable: Gettable<Key: Key, Value: CodecShared + Clone, Error = Error> {
129 fn new_batch(&self) -> Batch<'_, Self::Key, Self::Value, Self>
131 where
132 Self: Sized + Sync,
133 Self::Value: Send + Sync,
134 {
135 Batch {
136 db: self,
137 diff: BTreeMap::new(),
138 }
139 }
140
141 fn write_batch<'a, Iter>(
143 &'a mut self,
144 iter: Iter,
145 ) -> impl Future<Output = Result<(), Error>> + Send + use<'a, Self, Iter>
146 where
147 Self: Send,
148 Iter: IntoIterator<Item = (Self::Key, Option<Self::Value>)> + Send + 'a,
149 Iter::IntoIter: Send;
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155 use crate::{
156 kv::tests::{assert_deletable, assert_gettable, assert_updatable},
157 qmdb::store::db::Db,
158 translator::TwoCap,
159 };
160 use commonware_cryptography::sha256::Digest;
161 use commonware_runtime::deterministic::Context;
162
163 type TestStore = Db<Context, Digest, Vec<u8>, TwoCap>;
164 type TestBatch<'a> = Batch<'a, Digest, Vec<u8>, TestStore>;
165
166 #[allow(dead_code)]
167 fn assert_batch_futures_are_send(batch: &mut TestBatch<'_>, key: Digest) {
168 assert_gettable(batch, &key);
169 assert_updatable(batch, key, vec![]);
170 assert_deletable(batch, key);
171 }
172}