io_streams/buffered/
buf_reader_line_writer.rs

1//! This file is derived from Rust's library/std/src/io/buffered at revision
2//! f7801d6c7cc19ab22bdebcc8efa894a564c53469.
3
4use super::buf_duplexer::BufDuplexerBackend;
5use super::{BufReaderLineWriterShim, IntoInnerError};
6use duplex::HalfDuplex;
7#[cfg(feature = "layered-io")]
8use layered_io::{Bufferable, HalfDuplexLayered};
9use std::fmt;
10#[cfg(read_initializer)]
11use std::io::Initializer;
12use std::io::{self, BufRead, IoSlice, IoSliceMut, Read, Write};
13#[cfg(not(windows))]
14use {
15    io_extras::os::rustix::{AsRawFd, RawFd},
16    io_lifetimes::{AsFd, BorrowedFd},
17};
18#[cfg(windows)]
19use {
20    io_extras::os::windows::{
21        AsHandleOrSocket, AsRawHandleOrSocket, BorrowedHandleOrSocket, RawHandleOrSocket,
22    },
23    io_lifetimes::{AsHandle, AsSocket, BorrowedHandle, BorrowedSocket},
24    std::os::windows::io::{AsRawHandle, AsRawSocket, RawHandle, RawSocket},
25};
26
27/// Wraps a reader and writer and buffers input and output to and from it,
28/// flushing the writer whenever a newline (`0x0a`, `'\n'`) is detected on
29/// output.
30///
31/// The [`BufDuplexer`] struct wraps a reader and writer and buffers their
32/// input and output. But it only does this batched write when it goes out of
33/// scope, or when the internal buffer is full. Sometimes, you'd prefer to
34/// write each line as it's completed, rather than the entire buffer at once.
35/// Enter `BufReaderLineWriter`. It does exactly that.
36///
37/// Like [`BufDuplexer`], a `BufReaderLineWriter`’s buffer will also be flushed
38/// when the `BufReaderLineWriter` goes out of scope or when its internal
39/// buffer is full.
40///
41/// If there's still a partial line in the buffer when the
42/// `BufReaderLineWriter` is dropped, it will flush those contents.
43///
44/// # Examples
45///
46/// We can use `BufReaderLineWriter` to write one line at a time, significantly
47/// reducing the number of actual writes to the file.
48///
49/// ```no_run
50/// use char_device::CharDevice;
51/// use io_streams::BufReaderLineWriter;
52/// use std::fs;
53/// use std::io::prelude::*;
54///
55/// fn main() -> std::io::Result<()> {
56///     let road_not_taken = b"I shall be telling this with a sigh
57/// Somewhere ages and ages hence:
58/// Two roads diverged in a wood, and I -
59/// I took the one less traveled by,
60/// And that has made all the difference.";
61///
62///     let file = CharDevice::open("/dev/tty")?;
63///     let mut file = BufReaderLineWriter::new(file);
64///
65///     file.write_all(b"I shall be telling this with a sigh")?;
66///
67///     // No bytes are written until a newline is encountered (or
68///     // the internal buffer is filled).
69///     assert_eq!(fs::read_to_string("poem.txt")?, "");
70///     file.write_all(b"\n")?;
71///     assert_eq!(
72///         fs::read_to_string("poem.txt")?,
73///         "I shall be telling this with a sigh\n",
74///     );
75///
76///     // Write the rest of the poem.
77///     file.write_all(
78///         b"Somewhere ages and ages hence:
79/// Two roads diverged in a wood, and I -
80/// I took the one less traveled by,
81/// And that has made all the difference.",
82///     )?;
83///
84///     // The last line of the poem doesn't end in a newline, so
85///     // we have to flush or drop the `BufReaderLineWriter` to finish
86///     // writing.
87///     file.flush()?;
88///
89///     // Confirm the whole poem was written.
90///     assert_eq!(fs::read("poem.txt")?, &road_not_taken[..]);
91///     Ok(())
92/// }
93/// ```
94///
95/// [`BufDuplexer`]: crate::BufDuplexer
96pub struct BufReaderLineWriter<Inner: HalfDuplex> {
97    inner: BufReaderLineWriterBackend<Inner>,
98}
99
100/// The "backend" of `BufReaderLineWriter`, split off so that the public
101/// `BufReaderLineWriter` functions can do extra flushing, and we can
102/// use the private `BufReaderLineWriterBackend` functions internally
103/// after flushing is already done.
104struct BufReaderLineWriterBackend<Inner: HalfDuplex> {
105    inner: BufDuplexerBackend<Inner>,
106}
107
108impl<Inner: HalfDuplex> BufReaderLineWriter<Inner> {
109    /// Creates a new `BufReaderLineWriter`.
110    ///
111    /// # Examples
112    ///
113    /// ```no_run
114    /// use char_device::CharDevice;
115    /// use io_streams::BufReaderLineWriter;
116    ///
117    /// fn main() -> std::io::Result<()> {
118    ///     let file = CharDevice::open("/dev/tty")?;
119    ///     let file = BufReaderLineWriter::new(file);
120    ///     Ok(())
121    /// }
122    /// ```
123    #[inline]
124    pub fn new(inner: Inner) -> Self {
125        Self {
126            inner: BufReaderLineWriterBackend::new(inner),
127        }
128    }
129
130    /// Creates a new `BufReaderLineWriter` with a specified capacities for the
131    /// internal buffers.
132    ///
133    /// # Examples
134    ///
135    /// ```no_run
136    /// use char_device::CharDevice;
137    /// use io_streams::BufReaderLineWriter;
138    ///
139    /// fn main() -> std::io::Result<()> {
140    ///     let file = CharDevice::open("/dev/tty")?;
141    ///     let file = BufReaderLineWriter::with_capacities(10, 100, file);
142    ///     Ok(())
143    /// }
144    /// ```
145    #[inline]
146    pub fn with_capacities(reader_capacity: usize, writer_capacity: usize, inner: Inner) -> Self {
147        Self {
148            inner: BufReaderLineWriterBackend::with_capacities(
149                reader_capacity,
150                writer_capacity,
151                inner,
152            ),
153        }
154    }
155
156    /// Gets a reference to the underlying writer.
157    ///
158    /// # Examples
159    ///
160    /// ```no_run
161    /// use char_device::CharDevice;
162    /// use io_streams::BufReaderLineWriter;
163    ///
164    /// fn main() -> std::io::Result<()> {
165    ///     let file = CharDevice::open("/dev/tty")?;
166    ///     let file = BufReaderLineWriter::new(file);
167    ///
168    ///     let reference = file.get_ref();
169    ///     Ok(())
170    /// }
171    /// ```
172    #[inline]
173    pub fn get_ref(&self) -> &Inner {
174        self.inner.get_ref()
175    }
176
177    /// Gets a mutable reference to the underlying writer.
178    ///
179    /// Caution must be taken when calling methods on the mutable reference
180    /// returned as extra writes could corrupt the output stream.
181    ///
182    /// # Examples
183    ///
184    /// ```no_run
185    /// use char_device::CharDevice;
186    /// use io_streams::BufReaderLineWriter;
187    ///
188    /// fn main() -> std::io::Result<()> {
189    ///     let file = CharDevice::open("/dev/tty")?;
190    ///     let mut file = BufReaderLineWriter::new(file);
191    ///
192    ///     // we can use reference just like file
193    ///     let reference = file.get_mut();
194    ///     Ok(())
195    /// }
196    /// ```
197    #[inline]
198    pub fn get_mut(&mut self) -> &mut Inner {
199        self.inner.get_mut()
200    }
201
202    /// Unwraps this `BufReaderLineWriter`, returning the underlying writer.
203    ///
204    /// The internal buffer is written out before returning the writer.
205    ///
206    /// # Errors
207    ///
208    /// An [`Err`] will be returned if an error occurs while flushing the
209    /// buffer.
210    ///
211    /// # Examples
212    ///
213    /// ```no_run
214    /// use char_device::CharDevice;
215    /// use io_streams::BufReaderLineWriter;
216    ///
217    /// fn main() -> std::io::Result<()> {
218    ///     let file = CharDevice::open("/dev/tty")?;
219    ///
220    ///     let writer: BufReaderLineWriter<CharDevice> = BufReaderLineWriter::new(file);
221    ///
222    ///     let file: CharDevice = writer.into_inner()?;
223    ///     Ok(())
224    /// }
225    /// ```
226    #[inline]
227    pub fn into_inner(self) -> Result<Inner, IntoInnerError<Self>> {
228        self.inner
229            .into_inner()
230            .map_err(|err| err.new_wrapped(|inner| Self { inner }))
231    }
232}
233
234impl<Inner: HalfDuplex> BufReaderLineWriterBackend<Inner> {
235    pub fn new(inner: Inner) -> Self {
236        // Lines typically aren't that long, don't use giant buffers
237        Self::with_capacities(1024, 1024, inner)
238    }
239
240    pub fn with_capacities(reader_capacity: usize, writer_capacity: usize, inner: Inner) -> Self {
241        Self {
242            inner: BufDuplexerBackend::with_capacities(reader_capacity, writer_capacity, inner),
243        }
244    }
245
246    #[inline]
247    pub fn get_ref(&self) -> &Inner {
248        self.inner.get_ref()
249    }
250
251    #[inline]
252    pub fn get_mut(&mut self) -> &mut Inner {
253        self.inner.get_mut()
254    }
255
256    pub fn into_inner(self) -> Result<Inner, IntoInnerError<Self>> {
257        self.inner
258            .into_inner()
259            .map_err(|err| err.new_wrapped(|inner| Self { inner }))
260    }
261}
262
263impl<Inner: HalfDuplex> Write for BufReaderLineWriter<Inner> {
264    #[inline]
265    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
266        self.inner.write(buf)
267    }
268
269    #[inline]
270    fn flush(&mut self) -> io::Result<()> {
271        self.inner.flush()
272    }
273
274    #[inline]
275    fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
276        self.inner.write_vectored(bufs)
277    }
278
279    #[cfg(can_vector)]
280    #[inline]
281    fn is_write_vectored(&self) -> bool {
282        self.inner.is_write_vectored()
283    }
284
285    #[inline]
286    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
287        self.inner.write_all(buf)
288    }
289
290    #[cfg(write_all_vectored)]
291    #[inline]
292    fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
293        self.inner.write_all_vectored(bufs)
294    }
295
296    #[inline]
297    fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
298        self.inner.write_fmt(fmt)
299    }
300}
301
302impl<Inner: HalfDuplex> Write for BufReaderLineWriterBackend<Inner> {
303    #[inline]
304    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
305        BufReaderLineWriterShim::new(&mut self.inner).write(buf)
306    }
307
308    #[inline]
309    fn flush(&mut self) -> io::Result<()> {
310        self.inner.flush()
311    }
312
313    #[inline]
314    fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
315        BufReaderLineWriterShim::new(&mut self.inner).write_vectored(bufs)
316    }
317
318    #[cfg(can_vector)]
319    #[inline]
320    fn is_write_vectored(&self) -> bool {
321        self.inner.is_write_vectored()
322    }
323
324    #[inline]
325    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
326        BufReaderLineWriterShim::new(&mut self.inner).write_all(buf)
327    }
328
329    #[cfg(write_all_vectored)]
330    #[inline]
331    fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
332        BufReaderLineWriterShim::new(&mut self.inner).write_all_vectored(bufs)
333    }
334
335    #[inline]
336    fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
337        BufReaderLineWriterShim::new(&mut self.inner).write_fmt(fmt)
338    }
339}
340
341impl<Inner: HalfDuplex> Read for BufReaderLineWriter<Inner> {
342    #[inline]
343    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
344        // Flush the output buffer before reading.
345        self.inner.flush()?;
346
347        self.inner.read(buf)
348    }
349
350    #[inline]
351    fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
352        // Flush the output buffer before reading.
353        self.inner.flush()?;
354
355        self.inner.read_vectored(bufs)
356    }
357
358    #[cfg(can_vector)]
359    #[inline]
360    fn is_read_vectored(&self) -> bool {
361        self.inner.is_read_vectored()
362    }
363
364    #[cfg(read_initializer)]
365    #[inline]
366    unsafe fn initializer(&self) -> Initializer {
367        self.inner.initializer()
368    }
369}
370
371impl<Inner: HalfDuplex> Read for BufReaderLineWriterBackend<Inner> {
372    #[inline]
373    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
374        self.inner.read(buf)
375    }
376
377    #[inline]
378    fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
379        self.inner.read_vectored(bufs)
380    }
381
382    #[cfg(can_vector)]
383    #[inline]
384    fn is_read_vectored(&self) -> bool {
385        self.inner.is_read_vectored()
386    }
387
388    // we can't skip unconditionally because of the large buffer case in read.
389    #[cfg(read_initializer)]
390    #[inline]
391    unsafe fn initializer(&self) -> Initializer {
392        self.inner.initializer()
393    }
394}
395
396impl<Inner: HalfDuplex> BufRead for BufReaderLineWriter<Inner> {
397    #[inline]
398    fn fill_buf(&mut self) -> io::Result<&[u8]> {
399        self.inner.fill_buf()
400    }
401
402    #[inline]
403    fn consume(&mut self, amt: usize) {
404        self.inner.consume(amt)
405    }
406
407    #[inline]
408    fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
409        // Flush the output buffer before reading.
410        self.flush()?;
411
412        self.inner.read_until(byte, buf)
413    }
414
415    #[inline]
416    fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
417        // Flush the output buffer before reading.
418        self.flush()?;
419
420        let t = self.inner.read_line(buf)?;
421
422        Ok(t)
423    }
424}
425
426impl<Inner: HalfDuplex> BufRead for BufReaderLineWriterBackend<Inner> {
427    #[inline]
428    fn fill_buf(&mut self) -> io::Result<&[u8]> {
429        self.inner.fill_buf()
430    }
431
432    #[inline]
433    fn consume(&mut self, amt: usize) {
434        self.inner.consume(amt)
435    }
436}
437
438impl<Inner: HalfDuplex> fmt::Debug for BufReaderLineWriter<Inner>
439where
440    Inner: fmt::Debug,
441{
442    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
443        self.inner.fmt(fmt)
444    }
445}
446
447impl<Inner: HalfDuplex> fmt::Debug for BufReaderLineWriterBackend<Inner>
448where
449    Inner: fmt::Debug,
450{
451    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
452        fmt.debug_struct("BufReaderLineWriter")
453            .field("inner", &self.get_ref())
454            .field(
455                "reader_buffer",
456                &format_args!(
457                    "{}/{}",
458                    self.inner.reader_buffer().len(),
459                    self.inner.reader_capacity()
460                ),
461            )
462            .field(
463                "writer_buffer",
464                &format_args!(
465                    "{}/{}",
466                    self.inner.writer_buffer().len(),
467                    self.inner.writer_capacity()
468                ),
469            )
470            .finish()
471    }
472}
473
474#[cfg(not(windows))]
475impl<Inner: HalfDuplex + AsRawFd> AsRawFd for BufReaderLineWriter<Inner> {
476    #[inline]
477    fn as_raw_fd(&self) -> RawFd {
478        self.inner.as_raw_fd()
479    }
480}
481
482#[cfg(windows)]
483impl<Inner: HalfDuplex + AsRawHandle> AsRawHandle for BufReaderLineWriter<Inner> {
484    #[inline]
485    fn as_raw_handle(&self) -> RawHandle {
486        self.inner.as_raw_handle()
487    }
488}
489
490#[cfg(windows)]
491impl<Inner: HalfDuplex + AsRawSocket> AsRawSocket for BufReaderLineWriter<Inner> {
492    #[inline]
493    fn as_raw_socket(&self) -> RawSocket {
494        self.inner.as_raw_socket()
495    }
496}
497
498#[cfg(windows)]
499impl<Inner: HalfDuplex + AsRawHandleOrSocket> AsRawHandleOrSocket for BufReaderLineWriter<Inner> {
500    #[inline]
501    fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket {
502        self.inner.as_raw_handle_or_socket()
503    }
504}
505
506#[cfg(not(windows))]
507impl<Inner: HalfDuplex + AsRawFd> AsRawFd for BufReaderLineWriterBackend<Inner> {
508    #[inline]
509    fn as_raw_fd(&self) -> RawFd {
510        self.inner.as_raw_fd()
511    }
512}
513
514#[cfg(windows)]
515impl<Inner: HalfDuplex + AsRawHandle> AsRawHandle for BufReaderLineWriterBackend<Inner> {
516    #[inline]
517    fn as_raw_handle(&self) -> RawHandle {
518        self.inner.as_raw_handle()
519    }
520}
521
522#[cfg(windows)]
523impl<Inner: HalfDuplex + AsRawSocket> AsRawSocket for BufReaderLineWriterBackend<Inner> {
524    #[inline]
525    fn as_raw_socket(&self) -> RawSocket {
526        self.inner.as_raw_socket()
527    }
528}
529
530#[cfg(windows)]
531impl<Inner: HalfDuplex + AsRawHandleOrSocket> AsRawHandleOrSocket
532    for BufReaderLineWriterBackend<Inner>
533{
534    #[inline]
535    fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket {
536        self.inner.as_raw_handle_or_socket()
537    }
538}
539
540#[cfg(not(windows))]
541impl<Inner: HalfDuplex + AsFd> AsFd for BufReaderLineWriter<Inner> {
542    #[inline]
543    fn as_fd(&self) -> BorrowedFd<'_> {
544        self.inner.as_fd()
545    }
546}
547
548#[cfg(windows)]
549impl<Inner: HalfDuplex + AsHandle> AsHandle for BufReaderLineWriter<Inner> {
550    #[inline]
551    fn as_handle(&self) -> BorrowedHandle<'_> {
552        self.inner.as_handle()
553    }
554}
555
556#[cfg(windows)]
557impl<Inner: HalfDuplex + AsSocket> AsSocket for BufReaderLineWriter<Inner> {
558    #[inline]
559    fn as_socket(&self) -> BorrowedSocket<'_> {
560        self.inner.as_socket()
561    }
562}
563
564#[cfg(windows)]
565impl<Inner: HalfDuplex + AsHandleOrSocket> AsHandleOrSocket for BufReaderLineWriter<Inner> {
566    #[inline]
567    fn as_handle_or_socket(&self) -> BorrowedHandleOrSocket<'_> {
568        self.inner.as_handle_or_socket()
569    }
570}
571
572#[cfg(not(windows))]
573impl<Inner: HalfDuplex + AsFd> AsFd for BufReaderLineWriterBackend<Inner> {
574    #[inline]
575    fn as_fd(&self) -> BorrowedFd<'_> {
576        self.inner.as_fd()
577    }
578}
579
580#[cfg(windows)]
581impl<Inner: HalfDuplex + AsHandle> AsHandle for BufReaderLineWriterBackend<Inner> {
582    #[inline]
583    fn as_handle(&self) -> BorrowedHandle<'_> {
584        self.inner.as_handle()
585    }
586}
587
588#[cfg(windows)]
589impl<Inner: HalfDuplex + AsSocket> AsSocket for BufReaderLineWriterBackend<Inner> {
590    #[inline]
591    fn as_socket(&self) -> BorrowedSocket<'_> {
592        self.inner.as_socket()
593    }
594}
595
596#[cfg(windows)]
597impl<Inner: HalfDuplex + AsHandleOrSocket> AsHandleOrSocket for BufReaderLineWriterBackend<Inner> {
598    #[inline]
599    fn as_handle_or_socket(&self) -> BorrowedHandleOrSocket<'_> {
600        self.inner.as_handle_or_socket()
601    }
602}
603
604#[cfg(feature = "terminal-io")]
605impl<Inner: HalfDuplex + terminal_io::Terminal> terminal_io::Terminal
606    for BufReaderLineWriter<Inner>
607{
608}
609
610#[cfg(feature = "terminal-io")]
611impl<Inner: HalfDuplex + terminal_io::Terminal> terminal_io::Terminal
612    for BufReaderLineWriterBackend<Inner>
613{
614}
615
616#[cfg(feature = "terminal-io")]
617impl<Inner: HalfDuplex + terminal_io::WriteTerminal> terminal_io::WriteTerminal
618    for BufReaderLineWriter<Inner>
619{
620    #[inline]
621    fn color_support(&self) -> terminal_io::TerminalColorSupport {
622        self.inner.color_support()
623    }
624
625    #[inline]
626    fn color_preference(&self) -> bool {
627        self.inner.color_preference()
628    }
629
630    #[inline]
631    fn is_output_terminal(&self) -> bool {
632        self.inner.is_output_terminal()
633    }
634}
635
636#[cfg(feature = "terminal-io")]
637impl<Inner: HalfDuplex + terminal_io::WriteTerminal> terminal_io::WriteTerminal
638    for BufReaderLineWriterBackend<Inner>
639{
640    #[inline]
641    fn color_support(&self) -> terminal_io::TerminalColorSupport {
642        self.inner.color_support()
643    }
644
645    #[inline]
646    fn color_preference(&self) -> bool {
647        self.inner.color_preference()
648    }
649
650    #[inline]
651    fn is_output_terminal(&self) -> bool {
652        self.inner.is_output_terminal()
653    }
654}
655
656#[cfg(feature = "layered-io")]
657impl<Inner: HalfDuplexLayered> Bufferable for BufReaderLineWriter<Inner> {
658    #[inline]
659    fn abandon(&mut self) {
660        self.inner.abandon()
661    }
662
663    #[inline]
664    fn suggested_buffer_size(&self) -> usize {
665        self.inner.suggested_buffer_size()
666    }
667}
668
669#[cfg(feature = "layered-io")]
670impl<Inner: HalfDuplexLayered> Bufferable for BufReaderLineWriterBackend<Inner> {
671    #[inline]
672    fn abandon(&mut self) {
673        self.inner.abandon()
674    }
675
676    #[inline]
677    fn suggested_buffer_size(&self) -> usize {
678        self.inner.suggested_buffer_size()
679    }
680}