commonware_storage/qmdb/keyless/operation/
mod.rs1use crate::{
2 merkle::{Family, Location},
3 qmdb::{
4 any::value::ValueEncoding,
5 operation::{Committable, Floored},
6 },
7};
8use commonware_codec::{Encode as _, Error as CodecError, Read, Write};
9use commonware_formatting::hex;
10use commonware_runtime::{Buf, BufMut};
11use core::fmt::Display;
12
13pub(crate) mod fixed;
14pub(crate) mod variable;
15
16const COMMIT_CONTEXT: u8 = 0;
18const APPEND_CONTEXT: u8 = 1;
19
20pub trait Codec: ValueEncoding + Sized {
27 type ReadCfg: Clone + Send + Sync + 'static;
28
29 fn write_operation<F: Family>(op: &Operation<F, Self>, buf: &mut impl BufMut);
30 fn read_operation<F: Family>(
31 buf: &mut impl Buf,
32 cfg: &Self::ReadCfg,
33 ) -> Result<Operation<F, Self>, CodecError>;
34}
35
36#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
38pub enum Operation<F: Family, V: ValueEncoding> {
39 Append(V::Value),
41
42 Commit(Option<V::Value>, Location<F>),
45}
46
47impl<F: Family, V: ValueEncoding> Operation<F, V> {
48 pub fn into_value(self) -> Option<V::Value> {
50 match self {
51 Self::Append(value) => Some(value),
52 Self::Commit(value, _) => value,
53 }
54 }
55
56 pub const fn has_floor(&self) -> Option<Location<F>> {
58 match self {
59 Self::Commit(_, loc) => Some(*loc),
60 Self::Append(_) => None,
61 }
62 }
63}
64
65impl<F: Family, V: Codec> Write for Operation<F, V> {
66 fn write(&self, buf: &mut impl BufMut) {
67 V::write_operation(self, buf)
68 }
69}
70
71impl<F: Family, V: Codec> Committable for Operation<F, V> {
72 fn is_commit(&self) -> bool {
73 matches!(self, Self::Commit(_, _))
74 }
75}
76
77impl<F: Family, V: ValueEncoding> Floored<F> for Operation<F, V> {
78 fn has_floor(&self) -> Option<Location<F>> {
79 self.has_floor()
80 }
81}
82
83impl<F: Family, V: Codec> Read for Operation<F, V> {
84 type Cfg = <V as Codec>::ReadCfg;
85
86 fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, CodecError> {
87 V::read_operation(buf, cfg)
88 }
89}
90
91impl<F: Family, V: ValueEncoding> Display for Operation<F, V> {
92 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93 match self {
94 Self::Append(value) => write!(f, "[append value:{}]", hex(&value.encode())),
95 Self::Commit(value, floor) => {
96 if let Some(value) = value {
97 write!(f, "[commit {} floor:{}]", hex(&value.encode()), **floor)
98 } else {
99 write!(f, "[commit floor:{}]", **floor)
100 }
101 }
102 }
103 }
104}
105
106#[cfg(feature = "arbitrary")]
107impl<F: Family, V: ValueEncoding> arbitrary::Arbitrary<'_> for Operation<F, V>
108where
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 => Ok(Self::Append(V::Value::arbitrary(u)?)),
115 1 => {
116 let metadata = Option::<V::Value>::arbitrary(u)?;
117 let floor = Location::<F>::arbitrary(u)?;
118 Ok(Self::Commit(metadata, floor))
119 }
120 _ => unreachable!(),
121 }
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128 use crate::{merkle::mmr, qmdb::any::value::VariableEncoding};
129 use commonware_codec::Encode;
130 use commonware_formatting::hex;
131 use commonware_utils::sequence::U64;
132
133 #[test]
134 fn display_append() {
135 let op = Operation::<mmr::Family, VariableEncoding<U64>>::Append(U64::new(12345));
136 assert_eq!(
137 format!("{op}"),
138 format!("[append value:{}]", hex(&U64::new(12345).encode()))
139 );
140 }
141
142 #[test]
143 fn display_commit_some() {
144 let op = Operation::<mmr::Family, VariableEncoding<U64>>::Commit(
145 Some(U64::new(42)),
146 Location::new(7),
147 );
148 assert_eq!(
149 format!("{op}"),
150 format!("[commit {} floor:7]", hex(&U64::new(42).encode()))
151 );
152 }
153
154 #[test]
155 fn display_commit_none() {
156 let op = Operation::<mmr::Family, VariableEncoding<U64>>::Commit(None, Location::new(3));
157 assert_eq!(format!("{op}"), "[commit floor:3]");
158 }
159
160 #[cfg(feature = "arbitrary")]
161 mod conformance {
162 use super::Operation;
163 use crate::{
164 merkle::mmr,
165 qmdb::any::value::{FixedEncoding, VariableEncoding},
166 };
167 use commonware_codec::conformance::CodecConformance;
168 use commonware_utils::sequence::U64;
169
170 commonware_conformance::conformance_tests! {
171 CodecConformance<Operation<mmr::Family, VariableEncoding<U64>>>,
172 CodecConformance<Operation<mmr::Family, FixedEncoding<U64>>>
173 }
174 }
175}