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