try_next/
io.rs

1//! I/O utility implementations for `TryNext` and `TryNextWithContext`.
2//!
3//! This module provides implementations of the [`TryNext`] and
4//! [`TryNextWithContext`] traits for [`BufReader`], enabling incremental,
5//! fallible reading of bytes from any [`Read`] stream.
6//!
7//! These traits abstract over iterating through potentially fallible
8//! sources that yield elements asynchronously or sequentially. By
9//! implementing them for `BufReader`, this module allows buffered reading
10//! of single bytes in a way that works well for streaming parsers or
11//! byte-oriented protocols.
12//!
13//! # Features
14//!
15//! This module is only available when the **`std`** feature is enabled.
16//!
17//! # Example
18//!
19//! ```rust
20//! use std::io::BufReader;
21//! use try_next::{TryNext, TryNextWithContext};
22//!
23//! let data = b"abc";
24//! let mut reader = BufReader::new(&data[..]);
25//!
26//! assert_eq!(reader.try_next().unwrap(), Some(b'a'));
27//! assert_eq!(reader.try_next().unwrap(), Some(b'b'));
28//! assert_eq!(reader.try_next().unwrap(), Some(b'c'));
29//! assert_eq!(reader.try_next().unwrap(), None);
30//! ```
31
32#![cfg(feature = "std")]
33
34use crate::{TryNext, TryNextWithContext};
35use std::io::{self, BufRead, BufReader, Read};
36
37/// Implements [`TryNextWithContext`] for [`BufReader`].
38///
39/// This implementation enables reading the next byte from a buffered
40/// reader while optionally using a mutable external context `C`.
41///
42/// The context parameter allows stateful processing during iteration,
43/// even though this implementation does not directly use it.
44///
45/// # Type Parameters
46///
47/// * `R` — The underlying reader type implementing [`Read`].
48/// * `C` — The context type passed into [`try_next_with_context`].
49///
50/// # Errors
51///
52/// Returns any I/O error encountered during reading. Returns `Ok(None)`
53/// when the reader reaches the end of the input.
54impl<R, C> TryNextWithContext<C> for BufReader<R>
55where
56    R: Read,
57{
58    /// The type of item yielded — a single `u8` byte.
59    type Item = u8;
60
61    /// The error type produced by this iterator — [`io::Error`].
62    type Error = io::Error;
63
64    /// Attempts to read the next byte from the buffer using an optional context.
65    ///
66    /// # Returns
67    ///
68    /// * `Ok(Some(byte))` if a byte was read successfully.
69    /// * `Ok(None)` if EOF was reached.
70    /// * `Err(e)` if an I/O error occurred.
71    fn try_next_with_context(&mut self, _context: &mut C) -> Result<Option<Self::Item>, Self::Error> {
72        let buf = self.fill_buf()?;
73        if buf.is_empty() {
74            return Ok(None);
75        }
76        let b = buf[0];
77        self.consume(1);
78        Ok(Some(b))
79    }
80}
81
82/// Implements [`TryNext`] for [`BufReader`].
83///
84/// This version does not use a context, and simply reads the next byte
85/// sequentially from the buffer.
86///
87/// # Example
88///
89/// ```rust
90/// use std::io::BufReader;
91/// use try_next::TryNext;
92///
93/// let data = b"hi";
94/// let mut reader = BufReader::new(&data[..]);
95///
96/// assert_eq!(reader.try_next().unwrap(), Some(b'h'));
97/// assert_eq!(reader.try_next().unwrap(), Some(b'i'));
98/// assert_eq!(reader.try_next().unwrap(), None);
99/// ```
100impl<R> TryNext for BufReader<R>
101where
102    R: Read,
103{
104    /// The type of item yielded — a single `u8` byte.
105    type Item = u8;
106
107    /// The error type produced — [`io::Error`].
108    type Error = io::Error;
109
110    /// Attempts to read the next byte from the buffer.
111    ///
112    /// # Returns
113    ///
114    /// * `Ok(Some(byte))` if a byte was successfully read.
115    /// * `Ok(None)` if EOF was reached.
116    /// * `Err(e)` if an I/O error occurred.
117    fn try_next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
118        let buf = self.fill_buf()?;
119        if buf.is_empty() {
120            return Ok(None);
121        }
122        let b = buf[0];
123        self.consume(1);
124        Ok(Some(b))
125    }
126}
127
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132    use std::io::{self, Read};
133
134    #[test]
135    fn try_next_reads_each_byte_then_none() {
136        let data = b"abc";
137        let mut rdr = BufReader::new(&data[..]);
138
139        assert_eq!(rdr.try_next().unwrap(), Some(b'a'));
140        assert_eq!(rdr.try_next().unwrap(), Some(b'b'));
141        assert_eq!(rdr.try_next().unwrap(), Some(b'c'));
142        assert_eq!(rdr.try_next().unwrap(), None); // EOF
143        assert_eq!(rdr.try_next().unwrap(), None); // Stays at EOF
144    }
145
146    #[test]
147    fn try_next_with_context_reads_bytes_and_reaches_eof() {
148        let data = b"xy";
149        let mut rdr = BufReader::new(&data[..]);
150        let mut ctx = (); // context is unused; just verify it compiles/works
151
152        assert_eq!(rdr.try_next_with_context(&mut ctx).unwrap(), Some(b'x'));
153        assert_eq!(rdr.try_next_with_context(&mut ctx).unwrap(), Some(b'y'));
154        assert_eq!(rdr.try_next_with_context(&mut ctx).unwrap(), None);
155    }
156
157    #[test]
158    fn try_next_on_empty_input_is_none() {
159        let data: &[u8] = b"";
160        let mut rdr = BufReader::new(&data[..]);
161
162        assert_eq!(rdr.try_next().unwrap(), None);
163    }
164
165    #[test]
166    fn try_next_with_context_on_empty_input_is_none() {
167        let data: &[u8] = b"";
168        let mut rdr = BufReader::new(&data[..]);
169        let mut ctx = ();
170
171        assert_eq!(rdr.try_next_with_context(&mut ctx).unwrap(), None);
172    }
173
174    /// A reader that returns an error on first read to exercise error paths.
175    struct Boom;
176    impl Read for Boom {
177        fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
178            Err(io::Error::new(io::ErrorKind::Other, "boom"))
179        }
180    }
181
182    #[test]
183    fn try_next_propagates_errors() {
184        let mut rdr = BufReader::new(Boom);
185        let err = rdr.try_next().unwrap_err();
186        assert_eq!(err.kind(), io::ErrorKind::Other);
187        assert_eq!(err.to_string(), "boom");
188    }
189
190    #[test]
191    fn try_next_with_context_propagates_errors() {
192        let mut rdr = BufReader::new(Boom);
193        let mut ctx = ();
194        let err = rdr.try_next_with_context(&mut ctx).unwrap_err();
195        assert_eq!(err.kind(), io::ErrorKind::Other);
196        assert_eq!(err.to_string(), "boom");
197    }
198}
199