Skip to main content

gix_packetline/
read.rs

1#[cfg(any(feature = "blocking-io", feature = "async-io"))]
2use crate::MAX_LINE_LEN;
3use crate::{PacketLineRef, U16_HEX_BYTES};
4
5/// Allow the read-progress handler to determine how to continue.
6///
7/// Use [`std::ops::ControlFlow::Continue`] to continue reading the next progress if available.
8/// Use [`std::ops::ControlFlow::Break`] to abort all IO even if more would be available, claiming the operation was interrupted.
9pub type ProgressAction = std::ops::ControlFlow<()>;
10
11#[cfg(any(feature = "blocking-io", feature = "async-io"))]
12pub(crate) type ExhaustiveOutcome<'a> = (
13    bool,                                                                     // is_done
14    Option<PacketLineRef<'static>>,                                           // stopped_at
15    Option<std::io::Result<Result<PacketLineRef<'a>, crate::decode::Error>>>, // actual method result
16);
17
18mod error {
19    use std::fmt::{Debug, Display, Formatter};
20
21    use bstr::BString;
22
23    /// The error representing an ERR packet line, as possibly wrapped into an `std::io::Error`.
24    #[derive(Debug)]
25    pub struct Error {
26        /// The contents of the ERR line, with `ERR` portion stripped.
27        pub message: BString,
28    }
29
30    impl Display for Error {
31        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
32            Display::fmt(&self.message, f)
33        }
34    }
35
36    impl std::error::Error for Error {}
37}
38pub use error::Error;
39
40/// State for `StreamingPeekableIter` implementations.
41pub struct StreamingPeekableIterState<T> {
42    pub(crate) read: T,
43    pub(crate) peek_buf: Vec<u8>,
44    #[cfg(any(feature = "blocking-io", feature = "async-io"))]
45    pub(crate) buf: Vec<u8>,
46    pub(crate) fail_on_err_lines: bool,
47    pub(crate) delimiters: &'static [PacketLineRef<'static>],
48    pub(crate) is_done: bool,
49    pub(crate) stopped_at: Option<PacketLineRef<'static>>,
50    #[cfg_attr(all(not(feature = "async-io"), not(feature = "blocking-io")), allow(dead_code))]
51    pub(crate) trace: bool,
52}
53
54impl<T> StreamingPeekableIterState<T> {
55    /// Return a new instance from `read` which will stop decoding packet lines when receiving one of the given `delimiters`.
56    /// If `trace` is `true`, all packetlines received or sent will be passed to the facilities of the `gix-trace` crate.
57    #[cfg(any(feature = "blocking-io", feature = "async-io"))]
58    pub(crate) fn new(read: T, delimiters: &'static [PacketLineRef<'static>], trace: bool) -> Self {
59        Self {
60            read,
61            #[cfg(any(feature = "blocking-io", feature = "async-io"))]
62            buf: vec![0; MAX_LINE_LEN],
63            peek_buf: Vec::new(),
64            delimiters,
65            fail_on_err_lines: false,
66            is_done: false,
67            stopped_at: None,
68            trace,
69        }
70    }
71
72    /// Modify the peek buffer, overwriting the byte at `position` with the given byte to `replace_with` while truncating
73    /// it to contain only bytes until the newly replaced `position`.
74    ///
75    /// This is useful if you would want to remove 'special bytes' hidden behind, say a NULL byte to disappear and allow
76    /// standard line readers to read the next line as usual.
77    ///
78    /// **Note** that `position` does not include the 4 bytes prefix (they are invisible outside the reader)
79    pub fn peek_buffer_replace_and_truncate(&mut self, position: usize, replace_with: u8) {
80        let position = position + U16_HEX_BYTES;
81        self.peek_buf[position] = replace_with;
82
83        let new_len = position + 1;
84        self.peek_buf.truncate(new_len);
85        self.peek_buf[..4].copy_from_slice(&crate::encode::u16_to_hex((new_len) as u16));
86    }
87
88    /// Returns the packet line that stopped the iteration, or
89    /// `None` if the end wasn't reached yet, on EOF, or if [`fail_on_err_lines()`][StreamingPeekableIterState::fail_on_err_lines()] was true.
90    pub fn stopped_at(&self) -> Option<PacketLineRef<'static>> {
91        self.stopped_at
92    }
93
94    /// Reset all iteration state allowing to continue a stopped iteration that is not yet at EOF.
95    ///
96    /// This can happen once a delimiter is reached.
97    pub fn reset(&mut self) {
98        let delimiters = std::mem::take(&mut self.delimiters);
99        self.reset_with(delimiters);
100    }
101
102    /// Similar to [`reset()`][StreamingPeekableIterState::reset()] with support to changing the `delimiters`.
103    pub fn reset_with(&mut self, delimiters: &'static [PacketLineRef<'static>]) {
104        self.delimiters = delimiters;
105        self.is_done = false;
106        self.stopped_at = None;
107    }
108
109    /// If `value` is `true` the provider will check for special `ERR` packet lines and stop iteration when one is encountered.
110    ///
111    /// Use [`stopped_at()]`[`StreamingPeekableIterState::stopped_at()`] to inspect the cause of the end of the iteration.
112    /// ne
113    pub fn fail_on_err_lines(&mut self, value: bool) {
114        self.fail_on_err_lines = value;
115    }
116
117    /// Replace the reader used with the given `read`, resetting all other iteration state as well.
118    pub fn replace(&mut self, read: T) -> T {
119        let prev = std::mem::replace(&mut self.read, read);
120        self.reset();
121        self.fail_on_err_lines = false;
122        prev
123    }
124}