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>
95where
96 V::Value: Encode,
97{
98 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99 match self {
100 Self::Set(key, value) => {
101 write!(f, "[key:{} value:{}]", hex(key), hex(&value.encode()))
102 }
103 Self::Commit(value, floor) => {
104 if let Some(value) = value {
105 write!(f, "[commit {} floor:{}]", hex(&value.encode()), **floor)
106 } else {
107 write!(f, "[commit floor:{}]", **floor)
108 }
109 }
110 }
111 }
112}
113
114#[cfg(feature = "arbitrary")]
115impl<F: Family, K: Key, V: ValueEncoding> arbitrary::Arbitrary<'_> for Operation<F, K, V>
116where
117 K: for<'a> arbitrary::Arbitrary<'a>,
118 V::Value: for<'a> arbitrary::Arbitrary<'a>,
119{
120 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
121 let choice = u.int_in_range(0..=1)?;
122 match choice {
123 0 => {
124 let key = K::arbitrary(u)?;
125 let value = V::Value::arbitrary(u)?;
126 Ok(Self::Set(key, value))
127 }
128 1 => {
129 let metadata = Option::<V::Value>::arbitrary(u)?;
130 let max_loc = F::MAX_LEAVES;
131 let floor = u.int_in_range(0..=*max_loc)?;
132 Ok(Self::Commit(metadata, Location::new(floor)))
133 }
134 _ => unreachable!(),
135 }
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142 use crate::{merkle::mmr, qmdb::any::value::VariableEncoding};
143 use commonware_codec::Encode;
144 use commonware_utils::sequence::U64;
145
146 type VarOp = Operation<mmr::Family, U64, VariableEncoding<U64>>;
147
148 #[test]
149 fn test_operation_key() {
150 let key = U64::new(1234);
151 let value = U64::new(56789);
152
153 let set_op = VarOp::Set(key.clone(), value.clone());
154 assert_eq!(&key, set_op.key().unwrap());
155
156 let commit_op = VarOp::Commit(Some(value), Location::new(0));
157 assert_eq!(None, commit_op.key());
158
159 let commit_op_none = VarOp::Commit(None, Location::new(0));
160 assert_eq!(None, commit_op_none.key());
161 }
162
163 #[test]
164 fn test_operation_is_commit() {
165 let key = U64::new(1234);
166 let value = U64::new(56789);
167
168 let set_op = VarOp::Set(key, value.clone());
169 assert!(!set_op.is_commit());
170
171 let commit_op = VarOp::Commit(Some(value), Location::new(0));
172 assert!(commit_op.is_commit());
173
174 let commit_op_none = VarOp::Commit(None, Location::new(0));
175 assert!(commit_op_none.is_commit());
176 }
177
178 #[test]
179 fn test_operation_has_floor() {
180 let key = U64::new(1234);
181 let value = U64::new(56789);
182
183 let set_op = VarOp::Set(key, value.clone());
184 assert_eq!(<VarOp as Floored<mmr::Family>>::has_floor(&set_op), None);
185
186 let commit_op = VarOp::Commit(Some(value), Location::new(42));
187 assert_eq!(
188 <VarOp as Floored<mmr::Family>>::has_floor(&commit_op),
189 Some(Location::new(42))
190 );
191
192 let commit_op_none = VarOp::Commit(None, Location::new(0));
193 assert_eq!(
194 <VarOp as Floored<mmr::Family>>::has_floor(&commit_op_none),
195 Some(Location::new(0))
196 );
197 }
198
199 #[test]
200 fn test_operation_display() {
201 let key = U64::new(1234);
202 let value = U64::new(56789);
203
204 let set_op = VarOp::Set(key.clone(), value.clone());
205 assert_eq!(
206 format!("{set_op}"),
207 format!("[key:{} value:{}]", hex(&key), hex(&value.encode()))
208 );
209
210 let commit_op = VarOp::Commit(Some(value.clone()), Location::new(10));
211 assert_eq!(
212 format!("{commit_op}"),
213 format!("[commit {} floor:10]", hex(&value.encode()))
214 );
215
216 let commit_op = VarOp::Commit(None, Location::new(0));
217 assert_eq!(format!("{commit_op}"), "[commit floor:0]");
218 }
219}