]> git.proxmox.com Git - rustc.git/blob - vendor/gix-packetline/src/read/sidebands/blocking_io.rs
New upstream version 1.70.0+dfsg2
[rustc.git] / vendor / gix-packetline / src / read / sidebands / blocking_io.rs
1 use std::{io, io::BufRead};
2
3 use crate::{BandRef, PacketLineRef, StreamingPeekableIter, TextRef, U16_HEX_BYTES};
4
5 /// An implementor of [`BufRead`][io::BufRead] yielding packet lines on each call to [`read_line()`][io::BufRead::read_line()].
6 /// It's also possible to hide the underlying packet lines using the [`Read`][io::Read] implementation which is useful
7 /// if they represent binary data, like the one of a pack file.
8 pub struct WithSidebands<'a, T, F>
9 where
10 T: io::Read,
11 {
12 parent: &'a mut StreamingPeekableIter<T>,
13 handle_progress: Option<F>,
14 pos: usize,
15 cap: usize,
16 }
17
18 impl<'a, T, F> Drop for WithSidebands<'a, T, F>
19 where
20 T: io::Read,
21 {
22 fn drop(&mut self) {
23 self.parent.reset();
24 }
25 }
26
27 impl<'a, T> WithSidebands<'a, T, fn(bool, &[u8])>
28 where
29 T: io::Read,
30 {
31 /// Create a new instance with the given provider as `parent`.
32 pub fn new(parent: &'a mut StreamingPeekableIter<T>) -> Self {
33 WithSidebands {
34 parent,
35 handle_progress: None,
36 pos: 0,
37 cap: 0,
38 }
39 }
40 }
41
42 impl<'a, T, F> WithSidebands<'a, T, F>
43 where
44 T: io::Read,
45 F: FnMut(bool, &[u8]),
46 {
47 /// Create a new instance with the given `parent` provider and the `handle_progress` function.
48 ///
49 /// Progress or error information will be passed to the given `handle_progress(is_error, text)` function, with `is_error: bool`
50 /// being true in case the `text` is to be interpreted as error.
51 pub fn with_progress_handler(parent: &'a mut StreamingPeekableIter<T>, handle_progress: F) -> Self {
52 WithSidebands {
53 parent,
54 handle_progress: Some(handle_progress),
55 pos: 0,
56 cap: 0,
57 }
58 }
59
60 /// Create a new instance without a progress handler.
61 pub fn without_progress_handler(parent: &'a mut StreamingPeekableIter<T>) -> Self {
62 WithSidebands {
63 parent,
64 handle_progress: None,
65 pos: 0,
66 cap: 0,
67 }
68 }
69
70 /// Forwards to the parent [StreamingPeekableIter::reset_with()]
71 pub fn reset_with(&mut self, delimiters: &'static [PacketLineRef<'static>]) {
72 self.parent.reset_with(delimiters)
73 }
74
75 /// Forwards to the parent [StreamingPeekableIter::stopped_at()]
76 pub fn stopped_at(&self) -> Option<PacketLineRef<'static>> {
77 self.parent.stopped_at
78 }
79
80 /// Set or unset the progress handler.
81 pub fn set_progress_handler(&mut self, handle_progress: Option<F>) {
82 self.handle_progress = handle_progress;
83 }
84
85 /// Effectively forwards to the parent [StreamingPeekableIter::peek_line()], allowing to see what would be returned
86 /// next on a call to [`read_line()`][io::BufRead::read_line()].
87 ///
88 /// # Warning
89 ///
90 /// This skips all sideband handling and may return an unprocessed line with sidebands still contained in it.
91 pub fn peek_data_line(&mut self) -> Option<io::Result<Result<&[u8], crate::decode::Error>>> {
92 match self.parent.peek_line() {
93 Some(Ok(Ok(PacketLineRef::Data(line)))) => Some(Ok(Ok(line))),
94 Some(Ok(Err(err))) => Some(Ok(Err(err))),
95 Some(Err(err)) => Some(Err(err)),
96 _ => None,
97 }
98 }
99
100 /// Read a whole packetline from the underlying reader, with empty lines indicating a stop packetline.
101 ///
102 /// # Warning
103 ///
104 /// This skips all sideband handling and may return an unprocessed line with sidebands still contained in it.
105 pub fn read_data_line(&mut self) -> Option<io::Result<Result<PacketLineRef<'_>, crate::decode::Error>>> {
106 assert_eq!(
107 self.cap, 0,
108 "we don't support partial buffers right now - read-line must be used consistently"
109 );
110 self.parent.read_line()
111 }
112 }
113
114 impl<'a, T, F> BufRead for WithSidebands<'a, T, F>
115 where
116 T: io::Read,
117 F: FnMut(bool, &[u8]),
118 {
119 fn fill_buf(&mut self) -> io::Result<&[u8]> {
120 if self.pos >= self.cap {
121 let (ofs, cap) = loop {
122 let line = match self.parent.read_line() {
123 Some(line) => line?.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?,
124 None => break (0, 0),
125 };
126 match self.handle_progress.as_mut() {
127 Some(handle_progress) => {
128 let band = line
129 .decode_band()
130 .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
131 const ENCODED_BAND: usize = 1;
132 match band {
133 BandRef::Data(d) => {
134 if d.is_empty() {
135 continue;
136 }
137 break (U16_HEX_BYTES + ENCODED_BAND, d.len());
138 }
139 BandRef::Progress(d) => {
140 let text = TextRef::from(d).0;
141 handle_progress(false, text);
142 }
143 BandRef::Error(d) => {
144 let text = TextRef::from(d).0;
145 handle_progress(true, text);
146 }
147 };
148 }
149 None => {
150 break match line.as_slice() {
151 Some(d) => (U16_HEX_BYTES, d.len()),
152 None => {
153 return Err(io::Error::new(
154 io::ErrorKind::UnexpectedEof,
155 "encountered non-data line in a data-line only context",
156 ))
157 }
158 }
159 }
160 }
161 };
162 self.cap = cap + ofs;
163 self.pos = ofs;
164 }
165 Ok(&self.parent.buf[self.pos..self.cap])
166 }
167
168 fn consume(&mut self, amt: usize) {
169 self.pos = std::cmp::min(self.pos + amt, self.cap);
170 }
171
172 fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
173 assert_eq!(
174 self.cap, 0,
175 "we don't support partial buffers right now - read-line must be used consistently"
176 );
177 let line = std::str::from_utf8(self.fill_buf()?).map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
178 buf.push_str(line);
179 let bytes = line.len();
180 self.cap = 0;
181 Ok(bytes)
182 }
183 }
184
185 impl<'a, T, F> io::Read for WithSidebands<'a, T, F>
186 where
187 T: io::Read,
188 F: FnMut(bool, &[u8]),
189 {
190 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
191 let nread = {
192 let mut rem = self.fill_buf()?;
193 rem.read(buf)?
194 };
195 self.consume(nread);
196 Ok(nread)
197 }
198 }