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}