Skip to main content

stuffr_core/source/
borrowed.rs

1//! A [`Source`] over a borrowed, possibly non-`Send` reader.
2
3use std::io::Read;
4
5use super::{DecodeSideSource, SeekRead, Source, SourceCaps};
6use crate::archive::{Codec, DecodeOpts};
7use crate::error::{Error, Result};
8
9/// A forward-only source over any reader that lives for `'a`.
10///
11/// What [`super::ReaderSource`] is for an owned, `Send` reader, this is for a
12/// borrowed one: an archive member's reader (`Entry::reader`), a `&mut &[u8]`,
13/// a `&mut File`. Not seekable and length unknown, like `ReaderSource`, even
14/// over a reader that could seek: a decoder over it is told the truth about
15/// what it may rely on.
16pub struct BorrowedSource<'a> {
17    inner: Box<dyn Read + 'a>,
18}
19
20impl<'a> BorrowedSource<'a> {
21    pub fn new<R: Read + 'a>(r: R) -> Self {
22        Self { inner: Box::new(r) }
23    }
24}
25
26impl Read for BorrowedSource<'_> {
27    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
28        self.inner.read(buf)
29    }
30}
31
32impl Source for BorrowedSource<'_> {
33    fn caps(&self) -> SourceCaps {
34        SourceCaps {
35            seekable: false,
36            len: None,
37        }
38    }
39
40    fn as_seek(&mut self) -> Option<&mut dyn SeekRead> {
41        None
42    }
43}
44
45/// Decodes `r` with `codec`, borrowing `r` for as long as the decoder lives.
46///
47/// The one-call route for a caller holding a codec and a reader — notably an
48/// archive member's reader, which is neither `'static` nor `Send`. Needs no
49/// registry. A codec that cannot decode is refused with the same
50/// [`Error::CapabilityUnavailable`] `Registry::require_decoder` raises (exit 3).
51///
52/// Errors come back already classified, as on every other decode path: an
53/// `io::Error` from the decoder's construction is folded through
54/// [`Error::from_decode_io`], and the returned source is a
55/// [`DecodeSideSource`], so a malformed member read through it and converted
56/// with a bare `?` is the same [`Error::Corrupt`] (exit 5) as anywhere else,
57/// while a failure of `r` itself (a disk, a permission) stays [`Error::Io`].
58///
59/// **What the decode approximated is not in the returned source.** A codec
60/// records it in `o.fidelity` ([`crate::DecodeOpts::fidelity`]) — today
61/// [`crate::Fidelity::TrailingDataIgnored`], from gzip or bzip2, for bytes
62/// after the last verified member that were ignored — and only once it has
63/// reached the end of its stream. So keep a clone of the sink (or the
64/// `DecodeOpts` itself) and read it after the source has returned `Ok(0)`:
65/// [`crate::FidelitySink::warnings`], or [`crate::FidelitySink::drain_into`]
66/// a [`crate::FidelityReport`]. A caller that never looks loses the warning.
67pub fn decode_reader<'a, R: Read + 'a>(
68    codec: &dyn Codec,
69    r: R,
70    o: &DecodeOpts,
71) -> Result<Box<dyn Source + 'a>> {
72    if !codec.caps().decode {
73        return Err(Error::CapabilityUnavailable {
74            format: codec.id(),
75            available: "written",
76            requested: "read",
77        });
78    }
79    let decoded = codec
80        .decoder(Box::new(BorrowedSource::new(r)), o)
81        .map_err(|e| match e {
82            Error::Io(io_err) => Error::from_decode_io(io_err),
83            other => other,
84        })?;
85    Ok(DecodeSideSource::wrap(decoded))
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use crate::testing::MockCodec;
92
93    #[test]
94    fn a_borrowed_source_reads_a_local_borrow_and_never_claims_seek() {
95        let bytes = b"hello".to_vec();
96        let mut cursor = std::io::Cursor::new(&bytes[..]);
97        let mut s = BorrowedSource::new(&mut cursor);
98        assert!(
99            !s.caps().seekable,
100            "a Cursor can seek; the source must not say so"
101        );
102        assert_eq!(s.caps().len, None);
103        assert!(s.as_seek().is_none());
104        let mut out = Vec::new();
105        s.read_to_end(&mut out).unwrap();
106        assert_eq!(out, b"hello");
107    }
108
109    #[test]
110    fn decode_reader_decodes_through_a_non_send_borrow() {
111        // `Rc` makes the reader `!Send`; the borrow makes it non-`'static`.
112        struct NotSend<'b>(std::marker::PhantomData<std::rc::Rc<()>>, &'b [u8]);
113        impl Read for NotSend<'_> {
114            fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
115                self.1.read(buf)
116            }
117        }
118        let encoded: Vec<u8> = b"abc".iter().map(|b| b ^ 0xFF).collect();
119        let mut out = Vec::new();
120        decode_reader(
121            &MockCodec,
122            NotSend(std::marker::PhantomData, &encoded),
123            &DecodeOpts::default(),
124        )
125        .unwrap()
126        .read_to_end(&mut out)
127        .unwrap();
128        assert_eq!(out, b"abc");
129    }
130}