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