1use crate::{Error, Result};
7
8pub const FLUSH_PKT: &[u8] = b"0000";
10pub const DELIM_PKT: &[u8] = b"0001";
12pub const RESPONSE_END_PKT: &[u8] = b"0002";
14
15pub const MAX_PKT_LINE: usize = 65520;
17
18pub fn pkt_line(data: &[u8]) -> Vec<u8> {
20 let len = data.len() + 4; let mut pkt = format!("{:04x}", len).into_bytes();
22 pkt.extend_from_slice(data);
23 pkt
24}
25
26pub fn pkt_line_with_newline(data: &str) -> Vec<u8> {
28 let line = format!("{}\n", data);
29 pkt_line(line.as_bytes())
30}
31
32pub struct PktLineReader<'a> {
34 data: &'a [u8],
35 pos: usize,
36}
37
38impl<'a> PktLineReader<'a> {
39 pub fn new(data: &'a [u8]) -> Self {
40 Self { data, pos: 0 }
41 }
42
43 pub fn read(&mut self) -> Result<Option<PktLine<'a>>> {
45 if self.pos + 4 > self.data.len() {
46 return Ok(None);
47 }
48
49 let len_hex = std::str::from_utf8(&self.data[self.pos..self.pos + 4])
50 .map_err(|_| Error::ProtocolError("invalid pkt-line length".into()))?;
51
52 match len_hex {
54 "0000" => {
55 self.pos += 4;
56 return Ok(Some(PktLine::Flush));
57 }
58 "0001" => {
59 self.pos += 4;
60 return Ok(Some(PktLine::Delimiter));
61 }
62 "0002" => {
63 self.pos += 4;
64 return Ok(Some(PktLine::ResponseEnd));
65 }
66 _ => {}
67 }
68
69 let len = usize::from_str_radix(len_hex, 16)
70 .map_err(|_| Error::ProtocolError("invalid pkt-line length".into()))?;
71
72 if len < 4 {
73 return Err(Error::ProtocolError("pkt-line length too small".into()));
74 }
75
76 if len > MAX_PKT_LINE {
77 return Err(Error::ProtocolError("pkt-line too large".into()));
78 }
79
80 if self.pos + len > self.data.len() {
81 return Err(Error::ProtocolError("pkt-line truncated".into()));
82 }
83
84 let payload = &self.data[self.pos + 4..self.pos + len];
85 self.pos += len;
86
87 Ok(Some(PktLine::Data(payload)))
88 }
89
90 pub fn read_until_flush(&mut self) -> Result<Vec<&'a [u8]>> {
92 let mut lines = Vec::new();
93 loop {
94 match self.read()? {
95 Some(PktLine::Flush) | None => break,
96 Some(PktLine::Data(data)) => lines.push(data),
97 Some(PktLine::Delimiter) => continue,
98 Some(PktLine::ResponseEnd) => break,
99 }
100 }
101 Ok(lines)
102 }
103
104 pub fn remaining(&self) -> &'a [u8] {
106 &self.data[self.pos..]
107 }
108}
109
110#[derive(Debug, Clone, PartialEq, Eq)]
112pub enum PktLine<'a> {
113 Flush,
114 Delimiter,
115 ResponseEnd,
116 Data(&'a [u8]),
117}
118
119pub struct PktLineWriter {
121 buffer: Vec<u8>,
122}
123
124impl PktLineWriter {
125 pub fn new() -> Self {
126 Self { buffer: Vec::new() }
127 }
128
129 pub fn write(&mut self, data: &[u8]) {
130 self.buffer.extend_from_slice(&pkt_line(data));
131 }
132
133 pub fn write_str(&mut self, s: &str) {
134 self.buffer.extend_from_slice(&pkt_line_with_newline(s));
135 }
136
137 pub fn flush(&mut self) {
138 self.buffer.extend_from_slice(FLUSH_PKT);
139 }
140
141 pub fn delimiter(&mut self) {
142 self.buffer.extend_from_slice(DELIM_PKT);
143 }
144
145 pub fn response_end(&mut self) {
146 self.buffer.extend_from_slice(RESPONSE_END_PKT);
147 }
148
149 pub fn write_raw(&mut self, data: &[u8]) {
150 self.buffer.extend_from_slice(data);
151 }
152
153 pub fn into_bytes(self) -> Vec<u8> {
154 self.buffer
155 }
156
157 pub fn as_bytes(&self) -> &[u8] {
158 &self.buffer
159 }
160}
161
162impl Default for PktLineWriter {
163 fn default() -> Self {
164 Self::new()
165 }
166}
167
168pub fn parse_capabilities(caps_str: &str) -> Vec<String> {
170 caps_str.split(' ')
171 .filter(|s| !s.is_empty())
172 .map(|s| s.to_string())
173 .collect()
174}
175
176pub const UPLOAD_PACK_CAPABILITIES: &[&str] = &[
178 "multi_ack",
179 "multi_ack_detailed",
180 "side-band-64k",
181 "thin-pack",
182 "ofs-delta",
183 "shallow",
184 "no-progress",
185 "include-tag",
186 "allow-tip-sha1-in-want",
187 "allow-reachable-sha1-in-want",
188 "no-done",
189];
190
191pub const RECEIVE_PACK_CAPABILITIES: &[&str] = &[
193 "report-status",
194 "delete-refs",
195 "side-band-64k",
196 "ofs-delta",
197 "atomic",
198];
199
200pub fn format_capabilities(caps: &[&str]) -> String {
202 caps.join(" ")
203}
204
205pub mod sideband {
207 pub const DATA: u8 = 1;
208 pub const PROGRESS: u8 = 2;
209 pub const ERROR: u8 = 3;
210}
211
212pub fn sideband_pkt(channel: u8, data: &[u8]) -> Vec<u8> {
214 let mut payload = vec![channel];
215 payload.extend_from_slice(data);
216 pkt_line(&payload)
217}
218
219#[cfg(test)]
220mod tests {
221 use super::*;
222
223 #[test]
224 fn test_pkt_line() {
225 let pkt = pkt_line(b"hello");
226 assert_eq!(pkt, b"0009hello");
227 }
228
229 #[test]
230 fn test_pkt_line_with_newline() {
231 let pkt = pkt_line_with_newline("hello");
232 assert_eq!(pkt, b"000ahello\n");
233 }
234
235 #[test]
236 fn test_reader() {
237 let data = b"0009hello0006ab0000";
238 let mut reader = PktLineReader::new(data);
239
240 assert_eq!(reader.read().unwrap(), Some(PktLine::Data(b"hello")));
241 assert_eq!(reader.read().unwrap(), Some(PktLine::Data(b"ab")));
242 assert_eq!(reader.read().unwrap(), Some(PktLine::Flush));
243 assert_eq!(reader.read().unwrap(), None);
244 }
245
246 #[test]
247 fn test_writer() {
248 let mut writer = PktLineWriter::new();
249 writer.write_str("hello");
250 writer.flush();
251
252 assert_eq!(writer.as_bytes(), b"000ahello\n0000");
253 }
254}