use super::{Operation, COMMIT_CONTEXT, SET_CONTEXT}; use crate::qmdb::{ any::{value::VariableEncoding, VariableValue}, operation::Key, }; use commonware_codec::{EncodeSize, Error as CodecError, Read, ReadExt as _, Write}; use commonware_runtime::{Buf, BufMut}; impl EncodeSize for Operation> { fn encode_size(&self) -> usize { 1 + match self { Self::Set(k, v) => k.encode_size() + v.encode_size(), Self::Commit(v) => v.encode_size(), } } } impl Write for Operation> { fn write(&self, buf: &mut impl BufMut) { match &self { Self::Set(k, v) => { SET_CONTEXT.write(buf); k.write(buf); v.write(buf); } Self::Commit(v) => { COMMIT_CONTEXT.write(buf); v.write(buf); } } } } impl Read for Operation> { type Cfg = (::Cfg, ::Cfg); fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result { match u8::read(buf)? { SET_CONTEXT => { let key = K::read_cfg(buf, &cfg.0)?; let value = V::read_cfg(buf, &cfg.1)?; Ok(Self::Set(key, value)) } COMMIT_CONTEXT => Ok(Self::Commit(Option::::read_cfg(buf, &cfg.1)?)), e => Err(CodecError::InvalidEnum(e)), } } } #[cfg(test)] mod tests { use super::*; use commonware_codec::{DecodeExt, Encode, EncodeSize, FixedSize as _}; use commonware_utils::sequence::U64; type VarOp = Operation>; #[test] fn test_operation_encode_decode() { let key = U64::new(1234); let value = U64::new(56789); // Test Set operation let set_op = VarOp::Set(key, value.clone()); let encoded = set_op.encode(); let decoded = VarOp::decode(encoded).unwrap(); assert_eq!(set_op, decoded); // Test Commit operation with value let commit_op = VarOp::Commit(Some(value)); let encoded = commit_op.encode(); let decoded = VarOp::decode(encoded).unwrap(); assert_eq!(commit_op, decoded); // Test Commit operation without value let commit_op = VarOp::Commit(None); let encoded = commit_op.encode(); let decoded = VarOp::decode(encoded).unwrap(); assert_eq!(commit_op, decoded); } #[test] fn test_operation_encode_size() { let key = U64::new(1234); let value = U64::new(56789); let set_op = VarOp::Set(key, value.clone()); assert_eq!(set_op.encode_size(), 1 + U64::SIZE + value.encode_size()); assert_eq!(set_op.encode().len(), set_op.encode_size()); let commit_op = VarOp::Commit(Some(value.clone())); assert_eq!(commit_op.encode_size(), 1 + Some(value).encode_size()); assert_eq!(commit_op.encode().len(), commit_op.encode_size()); let commit_op = VarOp::Commit(None); assert_eq!( commit_op.encode_size(), 1 + Option::::None.encode_size() ); assert_eq!(commit_op.encode().len(), commit_op.encode_size()); } #[test] fn test_operation_invalid_context() { let invalid = vec![0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let decoded = VarOp::decode(invalid.as_ref()); assert!(matches!( decoded.unwrap_err(), CodecError::InvalidEnum(0xFF) )); } #[test] fn test_operation_insufficient_buffer() { let invalid = vec![SET_CONTEXT]; let decoded = VarOp::decode(invalid.as_ref()); assert!(matches!(decoded.unwrap_err(), CodecError::EndOfBuffer)); let invalid = vec![COMMIT_CONTEXT]; let decoded = VarOp::decode(invalid.as_ref()); assert!(matches!(decoded.unwrap_err(), CodecError::EndOfBuffer)); } #[test] fn test_operation_roundtrip_all_variants() { let key = U64::new(100); let value = U64::new(1000); let operations: Vec = vec![ VarOp::Set(key, value.clone()), VarOp::Commit(Some(value)), VarOp::Commit(None), ]; for op in operations { let encoded = op.encode(); let decoded = VarOp::decode(encoded.clone()).unwrap(); assert_eq!(op, decoded, "Failed to roundtrip: {op:?}"); assert_eq!(encoded.len(), op.encode_size(), "Size mismatch for: {op:?}"); } } #[test] fn test_operation_variable_key_roundtrip() { use commonware_codec::Decode as _; let key = vec![1u8, 2, 3, 4, 5]; let cfg = ((commonware_codec::RangeCfg::from(0..=100usize), ()), ()); // Test Set with variable-length key let set_op = Operation::Set(key, U64::new(42)); let encoded = set_op.encode(); assert_eq!(encoded.len(), set_op.encode_size()); let decoded = Operation::, VariableEncoding>::decode_cfg(encoded, &cfg).unwrap(); assert_eq!(set_op, decoded); // Test Commit (key-independent, should work the same) let commit_op = Operation::, VariableEncoding>::Commit(Some(U64::new(42))); let encoded = commit_op.encode(); let decoded = Operation::, VariableEncoding>::decode_cfg(encoded, &cfg).unwrap(); assert_eq!(commit_op, decoded); // Test empty key let empty_key_op = Operation::Set(vec![], U64::new(99)); let encoded = empty_key_op.encode(); let decoded = Operation::, VariableEncoding>::decode_cfg(encoded, &cfg).unwrap(); assert_eq!(empty_key_op, decoded); } #[cfg(feature = "arbitrary")] mod conformance { use super::*; use commonware_codec::conformance::CodecConformance; type VarKeyOp = Operation, VariableEncoding>; commonware_conformance::conformance_tests! { CodecConformance, CodecConformance } } }