Skip to content

Commit 721c215

Browse files
committed
First rough implementation of packet line writer
1 parent 922fcb6 commit 721c215

File tree

6 files changed

+103
-4
lines changed

6 files changed

+103
-4
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ Please see _'Development Status'_ for a listing of all crates and their capabili
123123
* [x] [V2 additions](https://github.com/git/git/blob/master/Documentation/technical/protocol-v2.txt#L35:L36)
124124
* [x] [side-band mode](https://github.com/git/git/blob/master/Documentation/technical/pack-protocol.txt#L467:L467)
125125
* [x] `Read` from packet line with (optional) progress support via sidebands
126+
* [ ] `Write` with built-in packet line encoding
126127

127128
### git-transport
128129
* [ ] general purpose `connect(…)` for clients

git-packetline/src/lib.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,8 @@ pub mod read;
3434
#[doc(inline)]
3535
pub use read::Reader;
3636

37-
pub mod write {
38-
/// An implementor of `Write` which passes all input to an inner `Write` in packet line data encoding.
39-
pub struct Writer {}
40-
}
37+
pub mod write;
38+
pub use write::Writer;
4139

4240
pub mod decode;
4341
pub mod encode;

git-packetline/src/write.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use std::io;
2+
3+
/// An implementor of `Write` which passes all input to an inner `Write` in packet line data encoding, one line per `write(…)`
4+
/// call or as many lines as it takes if the data doesn't fit into the maximum allowed line length.
5+
pub struct Writer<T> {
6+
pub inner: T,
7+
binary: bool,
8+
}
9+
10+
impl<T: io::Write> Writer<T> {
11+
pub fn new(inner: T) -> Self {
12+
Writer { inner, binary: true }
13+
}
14+
pub fn text_mode(mut self) -> Self {
15+
self.binary = false;
16+
self
17+
}
18+
pub fn binary_mode(mut self) -> Self {
19+
self.binary = true;
20+
self
21+
}
22+
}
23+
24+
impl<T: io::Write> io::Write for Writer<T> {
25+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
26+
if buf.is_empty() {
27+
return Err(io::Error::new(
28+
io::ErrorKind::Other,
29+
"empty packet lines are not permitted as '0004' is invalid",
30+
));
31+
}
32+
33+
if self.binary {
34+
crate::encode::data_to_write(buf, &mut self.inner)
35+
} else {
36+
crate::encode::text_to_write(buf, &mut self.inner)
37+
}
38+
.map_err(|err| {
39+
use crate::encode::Error::*;
40+
match err {
41+
Io(err) => err,
42+
DataIsEmpty | DataLengthLimitExceeded(_) => {
43+
unreachable!("We are handling empty and large data here, so this can't ever happen")
44+
}
45+
}
46+
})
47+
}
48+
49+
fn flush(&mut self) -> io::Result<()> {
50+
self.inner.flush()
51+
}
52+
}

git-packetline/tests/packet_line/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ fn assert_err_display<T: std::fmt::Debug, E: std::error::Error>(res: Result<T, E
88
mod decode;
99
mod encode;
1010
mod read;
11+
mod write;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
use bstr::ByteSlice;
2+
use git_packetline::Writer;
3+
use std::io::Write;
4+
5+
const MAX_DATA_LEN: usize = 65516;
6+
const MAX_LINE_LEN: usize = 4 + MAX_DATA_LEN;
7+
8+
#[test]
9+
fn each_write_results_in_one_line() -> crate::Result {
10+
let mut w = Writer::new(Vec::new());
11+
w.write(b"hello")?;
12+
w.write(b"world!")?;
13+
assert_eq!(w.inner.as_bstr(), b"0009hello000aworld!".as_bstr());
14+
Ok(())
15+
}
16+
17+
#[test]
18+
fn write_text_and_write_binary() -> crate::Result {
19+
let mut w = Writer::new(Vec::new()).text_mode();
20+
w.write(b"hello")?;
21+
w = w.binary_mode();
22+
w.write(b"world")?;
23+
assert_eq!(w.inner.as_bstr(), b"000ahello\n0009world".as_bstr());
24+
Ok(())
25+
}
26+
27+
#[test]
28+
fn huge_writes_are_split_into_lines() -> crate::Result {
29+
let data = {
30+
let mut v = Vec::new();
31+
v.resize(MAX_DATA_LEN * 2, 0);
32+
v
33+
};
34+
let mut w = Writer::new(Vec::new());
35+
w.write(&data)?;
36+
assert_eq!(w.inner.len(), MAX_LINE_LEN * 2);
37+
Ok(())
38+
}
39+
40+
#[test]
41+
fn empty_writes_fail_with_error() {
42+
assert_eq!(
43+
Writer::new(Vec::new()).write(&[]).unwrap_err().to_string(),
44+
"empty packet lines are not permitted as '0004' is invalid"
45+
)
46+
}

tasks.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
* [x] Disable sideband support (e.g. github http V2 doesn't have it)
1313
* [x] don't coerce line delimiters into empty slices.
1414
* [x] Make 'ERR <error>' handling as error optional, as it may occur only in certain contexts.
15+
* [ ] `Write` with packet line encoding
1516
* **git-url**
1617
* [ ] parse into components to make them easy to understand
1718
* **connect**

0 commit comments

Comments
 (0)