commonware_storage/qmdb/any/
ext.rs

1//! Extension type for simplified usage of Any authenticated databases.
2//!
3//! The [AnyExt] wrapper provides a traditional mutable key-value store interface over Any
4//! databases, automatically handling Clean/Dirty state transitions. This eliminates the need for
5//! manual state management while maintaining the performance benefits of deferred merkleization.
6
7use super::{CleanAny, DirtyAny};
8use crate::{
9    mmr::Location,
10    qmdb::{
11        store::{Batchable, CleanStore, DirtyStore, LogStore, LogStorePrunable},
12        Error,
13    },
14    store::{Store as StoreTrait, StoreDeletable, StoreMut, StorePersistable},
15};
16
17/// An extension wrapper for [CleanAny] databases that provides a traditional mutable key-value
18/// store interface by internally handling Clean/Dirty state transitions.
19pub struct AnyExt<A: CleanAny> {
20    // Invariant: always Some
21    inner: Option<State<A>>,
22}
23
24enum State<A: CleanAny> {
25    Clean(A),
26    Dirty(A::Dirty),
27}
28
29impl<A: CleanAny> AnyExt<A> {
30    /// Create a new wrapper from a Clean Any database.
31    pub const fn new(db: A) -> Self {
32        Self {
33            inner: Some(State::Clean(db)),
34        }
35    }
36
37    /// Close the database without destroying it. Uncommitted operations may be lost.
38    pub async fn close(mut self) -> Result<(), Error> {
39        // Merkleize before close
40        self.ensure_clean().await?;
41        match self.inner.take().expect("wrapper should never be empty") {
42            State::Clean(clean) => clean.close().await,
43            _ => unreachable!("ensure_clean guarantees Clean state"),
44        }
45    }
46
47    /// Ensure we're in dirty state, transitioning if necessary.
48    fn ensure_dirty(&mut self) {
49        let state = self.inner.take().expect("wrapper should never be empty");
50        self.inner = Some(match state {
51            State::Clean(clean) => State::Dirty(clean.into_dirty()),
52            State::Dirty(dirty) => State::Dirty(dirty),
53        });
54    }
55
56    /// Merkleize if in dirty state, ensuring we're in clean state.
57    async fn ensure_clean(&mut self) -> Result<(), Error> {
58        let state = self.inner.take().expect("wrapper should never be empty");
59        self.inner = Some(match state {
60            State::Clean(clean) => State::Clean(clean),
61            State::Dirty(dirty) => State::Clean(dirty.merkleize().await?),
62        });
63        Ok(())
64    }
65}
66
67impl<A> StoreTrait for AnyExt<A>
68where
69    A: CleanAny,
70{
71    type Key = A::Key;
72    type Value = <A as LogStore>::Value;
73    type Error = Error;
74
75    async fn get(&self, key: &Self::Key) -> Result<Option<Self::Value>, Self::Error> {
76        match self.inner.as_ref().expect("wrapper should never be empty") {
77            State::Clean(clean) => CleanAny::get(clean, key).await,
78            State::Dirty(dirty) => DirtyAny::get(dirty, key).await,
79        }
80    }
81}
82
83impl<A> StoreMut for AnyExt<A>
84where
85    A: CleanAny,
86{
87    async fn update(&mut self, key: Self::Key, value: Self::Value) -> Result<(), Self::Error> {
88        self.ensure_dirty();
89        match self.inner.as_mut().expect("wrapper should never be empty") {
90            State::Dirty(dirty) => DirtyAny::update(dirty, key, value).await,
91            _ => unreachable!("ensure_dirty guarantees Dirty state"),
92        }
93    }
94}
95
96impl<A> StoreDeletable for AnyExt<A>
97where
98    A: CleanAny,
99{
100    async fn delete(&mut self, key: Self::Key) -> Result<bool, Self::Error> {
101        self.ensure_dirty();
102        match self.inner.as_mut().expect("wrapper should never be empty") {
103            State::Dirty(dirty) => DirtyAny::delete(dirty, key).await,
104            _ => unreachable!("ensure_dirty guarantees Dirty state"),
105        }
106    }
107}
108
109impl<A> StorePersistable for AnyExt<A>
110where
111    A: CleanAny,
112{
113    async fn commit(&mut self) -> Result<(), Self::Error> {
114        // Merkleize before commit
115        self.ensure_clean().await?;
116        match self.inner.as_mut().expect("wrapper should never be empty") {
117            State::Clean(clean) => clean.commit(None).await.map(|_| ()),
118            _ => unreachable!("ensure_clean guarantees Clean state"),
119        }
120    }
121
122    async fn destroy(mut self) -> Result<(), Self::Error> {
123        // Merkleize before destroy
124        self.ensure_clean().await?;
125        match self.inner.take().expect("wrapper should never be empty") {
126            State::Clean(clean) => clean.destroy().await,
127            _ => unreachable!("ensure_clean guarantees Clean state"),
128        }
129    }
130}
131
132impl<A> LogStore for AnyExt<A>
133where
134    A: CleanAny,
135{
136    type Value = <A as LogStore>::Value;
137
138    fn is_empty(&self) -> bool {
139        match self.inner.as_ref().expect("wrapper should never be empty") {
140            State::Clean(clean) => clean.is_empty(),
141            State::Dirty(dirty) => dirty.is_empty(),
142        }
143    }
144
145    fn op_count(&self) -> Location {
146        match self.inner.as_ref().expect("wrapper should never be empty") {
147            State::Clean(clean) => clean.op_count(),
148            State::Dirty(dirty) => dirty.op_count(),
149        }
150    }
151
152    fn inactivity_floor_loc(&self) -> Location {
153        match self.inner.as_ref().expect("wrapper should never be empty") {
154            State::Clean(clean) => clean.inactivity_floor_loc(),
155            State::Dirty(dirty) => dirty.inactivity_floor_loc(),
156        }
157    }
158
159    async fn get_metadata(&self) -> Result<Option<Self::Value>, Error> {
160        match self.inner.as_ref().expect("wrapper should never be empty") {
161            State::Clean(clean) => clean.get_metadata().await,
162            State::Dirty(dirty) => dirty.get_metadata().await,
163        }
164    }
165}
166
167impl<A> LogStorePrunable for AnyExt<A>
168where
169    A: CleanAny,
170{
171    async fn prune(&mut self, prune_loc: Location) -> Result<(), Error> {
172        // Merkleize before prune
173        self.ensure_clean().await?;
174        match self.inner.as_mut().expect("wrapper should never be empty") {
175            State::Clean(clean) => clean.prune(prune_loc).await,
176            _ => unreachable!("ensure_clean guarantees Clean state"),
177        }
178    }
179}
180
181impl<A> Batchable for AnyExt<A>
182where
183    A: CleanAny,
184    <A as CleanStore>::Dirty: Batchable<Key = A::Key, Value = A::Value>,
185{
186    async fn write_batch(
187        &mut self,
188        iter: impl Iterator<Item = (Self::Key, Option<Self::Value>)>,
189    ) -> Result<(), Error> {
190        self.ensure_dirty();
191        match self.inner.as_mut().expect("wrapper should never be empty") {
192            State::Dirty(dirty) => dirty.write_batch(iter).await,
193            _ => unreachable!("ensure_dirty guarantees Dirty state"),
194        }
195    }
196}