1use crate::error::TypeError;
2use crate::fields::{
3 decode_bytes_value, decode_field_header, decode_varint_value, encode_bytes_field,
4 encode_nested_field, encode_varint_field, skip_field,
5};
6
7#[derive(Clone, Debug, PartialEq, Eq)]
27pub struct DiffBlock {
28 pub path: String,
29 pub hunks: Vec<DiffHunk>,
30}
31
32#[derive(Clone, Debug, PartialEq, Eq)]
49pub struct DiffHunk {
50 pub old_start: u32,
51 pub new_start: u32,
52 pub lines: Vec<u8>,
53}
54
55impl DiffHunk {
56 fn encode(&self) -> Vec<u8> {
58 let mut buf = Vec::new();
59 encode_varint_field(&mut buf, 1, u64::from(self.old_start));
60 encode_varint_field(&mut buf, 2, u64::from(self.new_start));
61 encode_bytes_field(&mut buf, 3, &self.lines);
62 buf
63 }
64
65 fn decode(mut buf: &[u8]) -> Result<Self, TypeError> {
67 let mut old_start: Option<u32> = None;
68 let mut new_start: Option<u32> = None;
69 let mut lines: Option<Vec<u8>> = None;
70
71 while !buf.is_empty() {
72 let (header, n) = decode_field_header(buf)?;
73 buf = &buf[n..];
74
75 match header.field_id {
76 1 => {
77 let (v, n) = decode_varint_value(buf)?;
78 buf = &buf[n..];
79 old_start = Some(v as u32);
80 }
81 2 => {
82 let (v, n) = decode_varint_value(buf)?;
83 buf = &buf[n..];
84 new_start = Some(v as u32);
85 }
86 3 => {
87 let (data, n) = decode_bytes_value(buf)?;
88 buf = &buf[n..];
89 lines = Some(data.to_vec());
90 }
91 _ => {
92 let n = skip_field(buf, header.wire_type)?;
93 buf = &buf[n..];
94 }
95 }
96 }
97
98 Ok(Self {
99 old_start: old_start.ok_or(TypeError::MissingRequiredField { field: "old_start" })?,
100 new_start: new_start.ok_or(TypeError::MissingRequiredField { field: "new_start" })?,
101 lines: lines.ok_or(TypeError::MissingRequiredField { field: "lines" })?,
102 })
103 }
104}
105
106impl DiffBlock {
107 pub fn encode_body(&self) -> Vec<u8> {
109 let mut buf = Vec::new();
110 encode_bytes_field(&mut buf, 1, self.path.as_bytes());
111 for hunk in &self.hunks {
112 encode_nested_field(&mut buf, 2, &hunk.encode());
113 }
114 buf
115 }
116
117 pub fn decode_body(mut buf: &[u8]) -> Result<Self, TypeError> {
119 let mut path: Option<String> = None;
120 let mut hunks = Vec::new();
121
122 while !buf.is_empty() {
123 let (header, n) = decode_field_header(buf)?;
124 buf = &buf[n..];
125
126 match header.field_id {
127 1 => {
128 let (data, n) = decode_bytes_value(buf)?;
129 buf = &buf[n..];
130 path = Some(String::from_utf8_lossy(data).into_owned());
131 }
132 2 => {
133 let (data, n) = decode_bytes_value(buf)?;
134 buf = &buf[n..];
135 hunks.push(DiffHunk::decode(data)?);
136 }
137 _ => {
138 let n = skip_field(buf, header.wire_type)?;
139 buf = &buf[n..];
140 }
141 }
142 }
143
144 Ok(Self {
145 path: path.ok_or(TypeError::MissingRequiredField { field: "path" })?,
146 hunks,
147 })
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn roundtrip_single_hunk() {
157 let block = DiffBlock {
158 path: "src/main.rs".to_string(),
159 hunks: vec![DiffHunk {
160 old_start: 10,
161 new_start: 10,
162 lines: b" fn main() {\n- println!(\"old\");\n+ println!(\"new\");\n }\n"
163 .to_vec(),
164 }],
165 };
166 let body = block.encode_body();
167 let decoded = DiffBlock::decode_body(&body).unwrap();
168 assert_eq!(decoded, block);
169 }
170
171 #[test]
172 fn roundtrip_multiple_hunks() {
173 let block = DiffBlock {
174 path: "lib.rs".to_string(),
175 hunks: vec![
176 DiffHunk {
177 old_start: 1,
178 new_start: 1,
179 lines: b"+use std::io;\n".to_vec(),
180 },
181 DiffHunk {
182 old_start: 50,
183 new_start: 51,
184 lines: b"- old_call();\n+ new_call();\n".to_vec(),
185 },
186 ],
187 };
188 let body = block.encode_body();
189 let decoded = DiffBlock::decode_body(&body).unwrap();
190 assert_eq!(decoded, block);
191 }
192
193 #[test]
194 fn empty_hunks() {
195 let block = DiffBlock {
196 path: "empty.rs".to_string(),
197 hunks: vec![],
198 };
199 let body = block.encode_body();
200 let decoded = DiffBlock::decode_body(&body).unwrap();
201 assert_eq!(decoded, block);
202 }
203}