1use claw_core::types::PatchOp;
2
3use crate::codec::Codec;
4use crate::PatchError;
5
6pub struct BinaryCodec;
8
9impl Codec for BinaryCodec {
10 fn id(&self) -> &str {
11 "binary"
12 }
13
14 fn diff(&self, old: &[u8], new: &[u8]) -> Result<Vec<PatchOp>, PatchError> {
15 if old == new {
16 return Ok(vec![]);
17 }
18 Ok(vec![PatchOp {
19 address: "B0".to_string(),
20 op_type: "replace".to_string(),
21 old_data: Some(old.to_vec()),
22 new_data: Some(new.to_vec()),
23 context_hash: None,
24 }])
25 }
26
27 fn apply(&self, base: &[u8], ops: &[PatchOp]) -> Result<Vec<u8>, PatchError> {
28 if ops.is_empty() {
29 return Ok(base.to_vec());
30 }
31 for op in ops.iter().rev() {
33 if let Some(ref new_data) = op.new_data {
34 return Ok(new_data.clone());
35 }
36 }
37 Err(PatchError::ApplyFailed("no replace op found".into()))
38 }
39
40 fn invert(&self, ops: &[PatchOp]) -> Result<Vec<PatchOp>, PatchError> {
41 Ok(ops
42 .iter()
43 .map(|op| PatchOp {
44 address: op.address.clone(),
45 op_type: op.op_type.clone(),
46 old_data: op.new_data.clone(),
47 new_data: op.old_data.clone(),
48 context_hash: None,
49 })
50 .collect())
51 }
52
53 fn commute(
54 &self,
55 _left: &[PatchOp],
56 _right: &[PatchOp],
57 ) -> Result<(Vec<PatchOp>, Vec<PatchOp>), PatchError> {
58 Err(PatchError::CommuteFailed)
59 }
60
61 fn merge3(&self, _base: &[u8], _left: &[u8], _right: &[u8]) -> Result<Vec<u8>, PatchError> {
62 Err(PatchError::Merge3Failed(
63 "binary files cannot be auto-merged".into(),
64 ))
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71
72 #[test]
73 fn binary_diff_identical() {
74 let codec = BinaryCodec;
75 let data = b"hello";
76 let ops = codec.diff(data, data).unwrap();
77 assert!(ops.is_empty());
78 }
79
80 #[test]
81 fn binary_diff_and_apply() {
82 let codec = BinaryCodec;
83 let old = b"old data";
84 let new = b"new data";
85 let ops = codec.diff(old, new).unwrap();
86 assert_eq!(ops.len(), 1);
87 let result = codec.apply(old, &ops).unwrap();
88 assert_eq!(result, new);
89 }
90
91 #[test]
92 fn binary_invert() {
93 let codec = BinaryCodec;
94 let old = b"old";
95 let new = b"new";
96 let ops = codec.diff(old, new).unwrap();
97 let inv = codec.invert(&ops).unwrap();
98 let result = codec.apply(new, &inv).unwrap();
99 assert_eq!(result, old);
100 }
101
102 #[test]
103 fn binary_commute_fails() {
104 let codec = BinaryCodec;
105 let ops = vec![PatchOp {
106 address: "B0".to_string(),
107 op_type: "replace".to_string(),
108 old_data: Some(vec![1]),
109 new_data: Some(vec![2]),
110 context_hash: None,
111 }];
112 assert!(codec.commute(&ops, &ops).is_err());
113 }
114
115 #[test]
116 fn binary_merge3_fails() {
117 let codec = BinaryCodec;
118 assert!(codec.merge3(b"base", b"left", b"right").is_err());
119 }
120}