gix_packetline/read/
blocking_io.rs

1use std::io;
2
3use bstr::ByteSlice;
4
5use crate::{
6    decode,
7    read::{ExhaustiveOutcome, ProgressAction, WithSidebands},
8    PacketLineRef, StreamingPeekableIter, MAX_LINE_LEN, U16_HEX_BYTES,
9};
10
11/// Non-IO methods
12impl<T> StreamingPeekableIter<T>
13where
14    T: io::Read,
15{
16    fn read_line_inner<'a>(reader: &mut T, buf: &'a mut [u8]) -> io::Result<Result<PacketLineRef<'a>, decode::Error>> {
17        let (hex_bytes, data_bytes) = buf.split_at_mut(4);
18        reader.read_exact(hex_bytes)?;
19        let num_data_bytes = match decode::hex_prefix(hex_bytes) {
20            Ok(decode::PacketLineOrWantedSize::Line(line)) => return Ok(Ok(line)),
21            Ok(decode::PacketLineOrWantedSize::Wanted(additional_bytes)) => additional_bytes as usize,
22            Err(err) => return Ok(Err(err)),
23        };
24
25        let (data_bytes, _) = data_bytes.split_at_mut(num_data_bytes);
26        reader.read_exact(data_bytes)?;
27        match decode::to_data_line(data_bytes) {
28            Ok(line) => Ok(Ok(line)),
29            Err(err) => Ok(Err(err)),
30        }
31    }
32
33    /// This function is needed to help the borrow checker allow us to return references all the time
34    /// It contains a bunch of logic shared between peek and `read_line` invocations.
35    fn read_line_inner_exhaustive<'a>(
36        reader: &mut T,
37        buf: &'a mut Vec<u8>,
38        delimiters: &[PacketLineRef<'static>],
39        fail_on_err_lines: bool,
40        buf_resize: bool,
41        trace: bool,
42    ) -> ExhaustiveOutcome<'a> {
43        (
44            false,
45            None,
46            Some(match Self::read_line_inner(reader, buf) {
47                Ok(Ok(line)) => {
48                    if trace {
49                        match line {
50                            #[allow(unused_variables)]
51                            PacketLineRef::Data(d) => {
52                                gix_trace::trace!("<< {}", d.as_bstr().trim().as_bstr());
53                            }
54                            PacketLineRef::Flush => {
55                                gix_trace::trace!("<< FLUSH");
56                            }
57                            PacketLineRef::Delimiter => {
58                                gix_trace::trace!("<< DELIM");
59                            }
60                            PacketLineRef::ResponseEnd => {
61                                gix_trace::trace!("<< RESPONSE_END");
62                            }
63                        }
64                    }
65                    if delimiters.contains(&line) {
66                        let stopped_at = delimiters.iter().find(|l| **l == line).copied();
67                        buf.clear();
68                        return (true, stopped_at, None);
69                    } else if fail_on_err_lines {
70                        if let Some(err) = line.check_error() {
71                            let err = err.0.as_bstr().to_owned();
72                            buf.clear();
73                            return (
74                                true,
75                                None,
76                                Some(Err(io::Error::new(
77                                    io::ErrorKind::Other,
78                                    crate::read::Error { message: err },
79                                ))),
80                            );
81                        }
82                    }
83                    let len = line.as_slice().map_or(U16_HEX_BYTES, |s| s.len() + U16_HEX_BYTES);
84                    if buf_resize {
85                        buf.resize(len, 0);
86                    }
87                    // TODO(borrowchk): remove additional decoding of internal buffer which is needed only to make it past borrowchk
88                    Ok(Ok(crate::decode(buf).expect("only valid data here")))
89                }
90                Ok(Err(err)) => {
91                    buf.clear();
92                    Ok(Err(err))
93                }
94                Err(err) => {
95                    buf.clear();
96                    Err(err)
97                }
98            }),
99        )
100    }
101
102    /// Read a packet line into the internal buffer and return it.
103    ///
104    /// Returns `None` if the end of iteration is reached because of one of the following:
105    ///
106    ///  * natural EOF
107    ///  * ERR packet line encountered if [`fail_on_err_lines()`][StreamingPeekableIter::fail_on_err_lines()] is true.
108    ///  * A `delimiter` packet line encountered
109    pub fn read_line(&mut self) -> Option<io::Result<Result<PacketLineRef<'_>, decode::Error>>> {
110        if self.is_done {
111            return None;
112        }
113        if !self.peek_buf.is_empty() {
114            std::mem::swap(&mut self.peek_buf, &mut self.buf);
115            self.peek_buf.clear();
116            Some(Ok(Ok(crate::decode(&self.buf).expect("only valid data in peek buf"))))
117        } else {
118            if self.buf.len() != MAX_LINE_LEN {
119                self.buf.resize(MAX_LINE_LEN, 0);
120            }
121            let (is_done, stopped_at, res) = Self::read_line_inner_exhaustive(
122                &mut self.read,
123                &mut self.buf,
124                self.delimiters,
125                self.fail_on_err_lines,
126                false,
127                self.trace,
128            );
129            self.is_done = is_done;
130            self.stopped_at = stopped_at;
131            res
132        }
133    }
134
135    /// Peek the next packet line without consuming it. Returns `None` if a stop-packet or an error
136    /// was encountered.
137    ///
138    /// Multiple calls to peek will return the same packet line, if there is one.
139    pub fn peek_line(&mut self) -> Option<io::Result<Result<PacketLineRef<'_>, decode::Error>>> {
140        if self.is_done {
141            return None;
142        }
143        if self.peek_buf.is_empty() {
144            self.peek_buf.resize(MAX_LINE_LEN, 0);
145            let (is_done, stopped_at, res) = Self::read_line_inner_exhaustive(
146                &mut self.read,
147                &mut self.peek_buf,
148                self.delimiters,
149                self.fail_on_err_lines,
150                true,
151                self.trace,
152            );
153            self.is_done = is_done;
154            self.stopped_at = stopped_at;
155            res
156        } else {
157            Some(Ok(Ok(crate::decode(&self.peek_buf).expect("only valid data here"))))
158        }
159    }
160
161    /// Return this instance as implementor of [`Read`][io::Read] assuming side bands to be used in all received packet lines.
162    /// Each invocation of [`read_line()`][io::BufRead::read_line()] returns a packet line.
163    ///
164    /// Progress or error information will be passed to the given `handle_progress(is_error, text)` function, with `is_error: bool`
165    /// being true in case the `text` is to be interpreted as error.
166    ///
167    /// _Please note_ that side bands need to be negotiated with the server.
168    pub fn as_read_with_sidebands<F: FnMut(bool, &[u8]) -> ProgressAction>(
169        &mut self,
170        handle_progress: F,
171    ) -> WithSidebands<'_, T, F> {
172        WithSidebands::with_progress_handler(self, handle_progress)
173    }
174
175    /// Same as [`as_read_with_sidebands(…)`][StreamingPeekableIter::as_read_with_sidebands()], but for channels without side band support.
176    ///
177    /// The type parameter `F` needs to be configured for this method to be callable using the 'turbofish' operator.
178    /// Use [`as_read()`][StreamingPeekableIter::as_read()].
179    pub fn as_read_without_sidebands<F: FnMut(bool, &[u8]) -> ProgressAction>(&mut self) -> WithSidebands<'_, T, F> {
180        WithSidebands::without_progress_handler(self)
181    }
182
183    /// Same as [`as_read_with_sidebands(…)`][StreamingPeekableIter::as_read_with_sidebands()], but for channels without side band support.
184    ///
185    /// Due to the preconfigured function type this method can be called without 'turbofish'.
186    #[allow(clippy::type_complexity)]
187    pub fn as_read(&mut self) -> WithSidebands<'_, T, fn(bool, &[u8]) -> ProgressAction> {
188        WithSidebands::new(self)
189    }
190}