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`].
58pub fn decode_reader<'a, R: Read + 'a>(
59    codec: &dyn Codec,
60    r: R,
61    o: &DecodeOpts,
62) -> Result<Box<dyn Source + 'a>> {
63    if !codec.caps().decode {
64        return Err(Error::CapabilityUnavailable {
65            format: codec.id(),
66            available: "written",
67            requested: "read",
68        });
69    }
70    let decoded = codec
71        .decoder(Box::new(BorrowedSource::new(r)), o)
72        .map_err(|e| match e {
73            Error::Io(io_err) => Error::from_decode_io(io_err),
74            other => other,
75        })?;
76    Ok(DecodeSideSource::wrap(decoded))
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82    use crate::testing::MockCodec;
83
84    #[test]
85    fn a_borrowed_source_reads_a_local_borrow_and_never_claims_seek() {
86        let bytes = b"hello".to_vec();
87        let mut cursor = std::io::Cursor::new(&bytes[..]);
88        let mut s = BorrowedSource::new(&mut cursor);
89        assert!(
90            !s.caps().seekable,
91            "a Cursor can seek; the source must not say so"
92        );
93        assert_eq!(s.caps().len, None);
94        assert!(s.as_seek().is_none());
95        let mut out = Vec::new();
96        s.read_to_end(&mut out).unwrap();
97        assert_eq!(out, b"hello");
98    }
99
100    #[test]
101    fn decode_reader_decodes_through_a_non_send_borrow() {
102        // `Rc` makes the reader `!Send`; the borrow makes it non-`'static`.
103        struct NotSend<'b>(std::marker::PhantomData<std::rc::Rc<()>>, &'b [u8]);
104        impl Read for NotSend<'_> {
105            fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
106                self.1.read(buf)
107            }
108        }
109        let encoded: Vec<u8> = b"abc".iter().map(|b| b ^ 0xFF).collect();
110        let mut out = Vec::new();
111        decode_reader(
112            &MockCodec,
113            NotSend(std::marker::PhantomData, &encoded),
114            &DecodeOpts::default(),
115        )
116        .unwrap()
117        .read_to_end(&mut out)
118        .unwrap();
119        assert_eq!(out, b"abc");
120    }
121}