commonware_storage/qmdb/keyless/operation/
mod.rs1use crate::qmdb::{any::value::ValueEncoding, operation::Committable};
2use commonware_codec::{Encode as _, Error as CodecError, Read, Write};
3use commonware_runtime::{Buf, BufMut};
4use commonware_utils::hex;
5use core::fmt::Display;
6
7pub(crate) mod fixed;
8pub(crate) mod variable;
9
10const COMMIT_CONTEXT: u8 = 0;
12const APPEND_CONTEXT: u8 = 1;
13
14pub trait Codec: ValueEncoding + Sized {
21 type ReadCfg: Clone + Send + Sync + 'static;
22
23 fn write_operation(op: &Operation<Self>, buf: &mut impl BufMut);
24 fn read_operation(
25 buf: &mut impl Buf,
26 cfg: &Self::ReadCfg,
27 ) -> Result<Operation<Self>, CodecError>;
28}
29
30#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
32pub enum Operation<V: ValueEncoding> {
33 Append(V::Value),
35
36 Commit(Option<V::Value>),
38}
39
40impl<V: ValueEncoding> Operation<V> {
41 pub fn into_value(self) -> Option<V::Value> {
43 match self {
44 Self::Append(value) => Some(value),
45 Self::Commit(value) => value,
46 }
47 }
48}
49
50impl<V: Codec> Write for Operation<V> {
51 fn write(&self, buf: &mut impl BufMut) {
52 V::write_operation(self, buf)
53 }
54}
55
56impl<V: Codec> Committable for Operation<V> {
57 fn is_commit(&self) -> bool {
58 matches!(self, Self::Commit(_))
59 }
60}
61
62impl<V: Codec> Read for Operation<V> {
63 type Cfg = <V as Codec>::ReadCfg;
64
65 fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, CodecError> {
66 V::read_operation(buf, cfg)
67 }
68}
69
70impl<V: ValueEncoding> Display for Operation<V> {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 match self {
73 Self::Append(value) => write!(f, "[append value:{}]", hex(&value.encode())),
74 Self::Commit(value) => {
75 if let Some(value) = value {
76 write!(f, "[commit {}]", hex(&value.encode()))
77 } else {
78 write!(f, "[commit]")
79 }
80 }
81 }
82 }
83}
84
85#[cfg(feature = "arbitrary")]
86impl<V: ValueEncoding> arbitrary::Arbitrary<'_> for Operation<V>
87where
88 V::Value: for<'a> arbitrary::Arbitrary<'a>,
89{
90 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
91 let choice = u.int_in_range(0..=1)?;
92 match choice {
93 0 => Ok(Self::Append(V::Value::arbitrary(u)?)),
94 1 => Ok(Self::Commit(Option::<V::Value>::arbitrary(u)?)),
95 _ => unreachable!(),
96 }
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103 use crate::qmdb::any::value::VariableEncoding;
104 use commonware_codec::Encode;
105 use commonware_utils::{hex, sequence::U64};
106
107 #[test]
108 fn display_append() {
109 let op = Operation::<VariableEncoding<U64>>::Append(U64::new(12345));
110 assert_eq!(
111 format!("{op}"),
112 format!("[append value:{}]", hex(&U64::new(12345).encode()))
113 );
114 }
115
116 #[test]
117 fn display_commit_some() {
118 let op = Operation::<VariableEncoding<U64>>::Commit(Some(U64::new(42)));
119 assert_eq!(
120 format!("{op}"),
121 format!("[commit {}]", hex(&U64::new(42).encode()))
122 );
123 }
124
125 #[test]
126 fn display_commit_none() {
127 let op = Operation::<VariableEncoding<U64>>::Commit(None);
128 assert_eq!(format!("{op}"), "[commit]");
129 }
130
131 #[cfg(feature = "arbitrary")]
132 mod conformance {
133 use super::Operation;
134 use crate::qmdb::any::value::{FixedEncoding, VariableEncoding};
135 use commonware_codec::conformance::CodecConformance;
136 use commonware_utils::sequence::U64;
137
138 commonware_conformance::conformance_tests! {
139 CodecConformance<Operation<VariableEncoding<U64>>>,
140 CodecConformance<Operation<FixedEncoding<U64>>>
141 }
142 }
143}