commonware_storage/qmdb/immutable/operation/
mod.rs1pub(crate) mod fixed;
10pub(crate) mod variable;
11
12use crate::{
13 merkle::{Family, Location},
14 qmdb::{
15 any::ValueEncoding,
16 operation::{Floored, Key, Operation as OperationTrait},
17 },
18};
19use commonware_codec::Encode;
20use commonware_formatting::hex;
21use core::fmt::Display;
22
23pub(crate) const SET_CONTEXT: u8 = 0;
25pub(crate) const COMMIT_CONTEXT: u8 = 1;
26
27#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
32pub enum Operation<F: Family, K: Key, V: ValueEncoding> {
33 Set(K, V::Value),
35
36 Commit(Option<V::Value>, Location<F>),
39}
40
41impl<F: Family, K: Key, V: ValueEncoding> Operation<F, K, V> {
42 pub const fn key(&self) -> Option<&K> {
44 match self {
45 Self::Set(key, _) => Some(key),
46 Self::Commit(_, _) => None,
47 }
48 }
49
50 pub const fn is_commit(&self) -> bool {
52 matches!(self, Self::Commit(_, _))
53 }
54
55 pub const fn has_floor(&self) -> Option<Location<F>> {
57 match self {
58 Self::Commit(_, loc) => Some(*loc),
59 Self::Set(_, _) => None,
60 }
61 }
62}
63
64impl<F: Family, K: Key, V: ValueEncoding> OperationTrait<F> for Operation<F, K, V> {
65 type Key = K;
66
67 fn key(&self) -> Option<&Self::Key> {
68 self.key()
69 }
70
71 fn into_key(self) -> Option<Self::Key> {
72 match self {
73 Self::Set(key, _) => Some(key),
74 Self::Commit(_, _) => None,
75 }
76 }
77
78 fn is_delete(&self) -> bool {
79 false
81 }
82
83 fn is_update(&self) -> bool {
84 matches!(self, Self::Set(_, _))
85 }
86}
87
88impl<F: Family, K: Key, V: ValueEncoding> Floored<F> for Operation<F, K, V> {
89 fn has_floor(&self) -> Option<Location<F>> {
90 self.has_floor()
91 }
92}
93
94impl<F: Family, K: Key, V: ValueEncoding> Display for Operation<F, K, V> {
95 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 match self {
97 Self::Set(key, value) => {
98 write!(f, "[key:{} value:{}]", hex(key), hex(&value.encode()))
99 }
100 Self::Commit(value, floor) => {
101 if let Some(value) = value {
102 write!(f, "[commit {} floor:{}]", hex(&value.encode()), **floor)
103 } else {
104 write!(f, "[commit floor:{}]", **floor)
105 }
106 }
107 }
108 }
109}
110
111#[cfg(feature = "arbitrary")]
112impl<F: Family, K: Key, V: ValueEncoding> arbitrary::Arbitrary<'_> for Operation<F, K, V>
113where
114 K: for<'a> arbitrary::Arbitrary<'a>,
115 V::Value: for<'a> arbitrary::Arbitrary<'a>,
116{
117 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
118 let choice = u.int_in_range(0..=1)?;
119 match choice {
120 0 => {
121 let key = K::arbitrary(u)?;
122 let value = V::Value::arbitrary(u)?;
123 Ok(Self::Set(key, value))
124 }
125 1 => {
126 let metadata = Option::<V::Value>::arbitrary(u)?;
127 let max_loc = F::MAX_LEAVES;
128 let floor = u.int_in_range(0..=*max_loc)?;
129 Ok(Self::Commit(metadata, Location::new(floor)))
130 }
131 _ => unreachable!(),
132 }
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139 use crate::{merkle::mmr, qmdb::any::value::VariableEncoding};
140 use commonware_codec::Encode;
141 use commonware_utils::sequence::U64;
142
143 type VarOp = Operation<mmr::Family, U64, VariableEncoding<U64>>;
144
145 #[test]
146 fn test_operation_key() {
147 let key = U64::new(1234);
148 let value = U64::new(56789);
149
150 let set_op = VarOp::Set(key.clone(), value.clone());
151 assert_eq!(&key, set_op.key().unwrap());
152
153 let commit_op = VarOp::Commit(Some(value), Location::new(0));
154 assert_eq!(None, commit_op.key());
155
156 let commit_op_none = VarOp::Commit(None, Location::new(0));
157 assert_eq!(None, commit_op_none.key());
158 }
159
160 #[test]
161 fn test_operation_is_commit() {
162 let key = U64::new(1234);
163 let value = U64::new(56789);
164
165 let set_op = VarOp::Set(key, value.clone());
166 assert!(!set_op.is_commit());
167
168 let commit_op = VarOp::Commit(Some(value), Location::new(0));
169 assert!(commit_op.is_commit());
170
171 let commit_op_none = VarOp::Commit(None, Location::new(0));
172 assert!(commit_op_none.is_commit());
173 }
174
175 #[test]
176 fn test_operation_has_floor() {
177 let key = U64::new(1234);
178 let value = U64::new(56789);
179
180 let set_op = VarOp::Set(key, value.clone());
181 assert_eq!(<VarOp as Floored<mmr::Family>>::has_floor(&set_op), None);
182
183 let commit_op = VarOp::Commit(Some(value), Location::new(42));
184 assert_eq!(
185 <VarOp as Floored<mmr::Family>>::has_floor(&commit_op),
186 Some(Location::new(42))
187 );
188
189 let commit_op_none = VarOp::Commit(None, Location::new(0));
190 assert_eq!(
191 <VarOp as Floored<mmr::Family>>::has_floor(&commit_op_none),
192 Some(Location::new(0))
193 );
194 }
195
196 #[test]
197 fn test_operation_display() {
198 let key = U64::new(1234);
199 let value = U64::new(56789);
200
201 let set_op = VarOp::Set(key.clone(), value.clone());
202 assert_eq!(
203 format!("{set_op}"),
204 format!("[key:{} value:{}]", hex(&key), hex(&value.encode()))
205 );
206
207 let commit_op = VarOp::Commit(Some(value.clone()), Location::new(10));
208 assert_eq!(
209 format!("{commit_op}"),
210 format!("[commit {} floor:10]", hex(&value.encode()))
211 );
212
213 let commit_op = VarOp::Commit(None, Location::new(0));
214 assert_eq!(format!("{commit_op}"), "[commit floor:0]");
215 }
216}