pub(crate) mod fixed;
pub(crate) mod variable;
use crate::{
merkle::{Family, Location},
qmdb::{
any::ValueEncoding,
operation::{Key, Operation as OperationTrait},
},
};
use commonware_codec::Encode;
use commonware_utils::hex;
use core::fmt::Display;
pub(crate) const SET_CONTEXT: u8 = 0;
pub(crate) const COMMIT_CONTEXT: u8 = 1;
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum Operation<K: Key, V: ValueEncoding> {
Set(K, V::Value),
Commit(Option<V::Value>),
}
impl<K: Key, V: ValueEncoding> Operation<K, V> {
pub const fn key(&self) -> Option<&K> {
match self {
Self::Set(key, _) => Some(key),
Self::Commit(_) => None,
}
}
pub const fn is_commit(&self) -> bool {
matches!(self, Self::Commit(_))
}
}
impl<F: Family, K: Key, V: ValueEncoding> OperationTrait<F> for Operation<K, V> {
type Key = K;
fn key(&self) -> Option<&Self::Key> {
self.key()
}
fn is_delete(&self) -> bool {
false
}
fn is_update(&self) -> bool {
matches!(self, Self::Set(_, _))
}
fn has_floor(&self) -> Option<Location<F>> {
None
}
}
impl<K: Key, V: ValueEncoding> Display for Operation<K, V>
where
V::Value: Encode,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Set(key, value) => {
write!(f, "[key:{} value:{}]", hex(key), hex(&value.encode()))
}
Self::Commit(value) => {
if let Some(value) = value {
write!(f, "[commit {}]", hex(&value.encode()))
} else {
write!(f, "[commit]")
}
}
}
}
}
#[cfg(feature = "arbitrary")]
impl<K: Key, V: ValueEncoding> arbitrary::Arbitrary<'_> for Operation<K, V>
where
K: for<'a> arbitrary::Arbitrary<'a>,
V::Value: for<'a> arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let choice = u.int_in_range(0..=1)?;
match choice {
0 => {
let key = K::arbitrary(u)?;
let value = V::Value::arbitrary(u)?;
Ok(Self::Set(key, value))
}
1 => Ok(Self::Commit(Option::<V::Value>::arbitrary(u)?)),
_ => unreachable!(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::qmdb::any::value::VariableEncoding;
use commonware_codec::Encode;
use commonware_utils::sequence::U64;
type VarOp = Operation<U64, VariableEncoding<U64>>;
#[test]
fn test_operation_key() {
let key = U64::new(1234);
let value = U64::new(56789);
let set_op = VarOp::Set(key.clone(), value.clone());
assert_eq!(&key, set_op.key().unwrap());
let commit_op = VarOp::Commit(Some(value));
assert_eq!(None, commit_op.key());
let commit_op_none = VarOp::Commit(None);
assert_eq!(None, commit_op_none.key());
}
#[test]
fn test_operation_is_commit() {
let key = U64::new(1234);
let value = U64::new(56789);
let set_op = VarOp::Set(key, value.clone());
assert!(!set_op.is_commit());
let commit_op = VarOp::Commit(Some(value));
assert!(commit_op.is_commit());
let commit_op_none = VarOp::Commit(None);
assert!(commit_op_none.is_commit());
}
#[test]
fn test_operation_display() {
let key = U64::new(1234);
let value = U64::new(56789);
let set_op = VarOp::Set(key.clone(), value.clone());
assert_eq!(
format!("{set_op}"),
format!("[key:{} value:{}]", hex(&key), hex(&value.encode()))
);
let commit_op = VarOp::Commit(Some(value.clone()));
assert_eq!(
format!("{commit_op}"),
format!("[commit {}]", hex(&value.encode()))
);
let commit_op = VarOp::Commit(None);
assert_eq!(format!("{commit_op}"), "[commit]");
}
}