stuffr_core/source/
mod.rs1mod file;
5pub mod limit;
6mod reader;
7pub mod spill;
8pub mod stream_only;
9
10pub use file::FileSource;
11pub use limit::{Counting, CountingWriter, DEFAULT_MAX_RATIO, RATIO_FLOOR, RatioGuard};
12pub use reader::{PeekSource, ReaderSource};
13pub use spill::{SpillPolicy, SpillSource};
14pub use stream_only::StreamOnly;
15
16use std::io::{Read, Seek};
17
18pub trait SeekRead: Read + Seek + Send {}
20impl<T: Read + Seek + Send> SeekRead for T {}
21
22#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
23pub struct SourceCaps {
24 pub seekable: bool,
25 pub len: Option<u64>,
27}
28
29pub trait Source: Read + Send {
31 fn caps(&self) -> SourceCaps;
32
33 fn as_seek(&mut self) -> Option<&mut dyn SeekRead>;
35}
36
37#[cfg(test)]
38mod tests {
39 use super::*;
40 use std::io::Read;
41
42 #[test]
43 fn reader_source_is_not_seekable_and_has_unknown_length() {
44 let mut s = ReaderSource::new(std::io::Cursor::new(b"hello".to_vec()));
45 assert!(!s.caps().seekable);
48 assert_eq!(s.caps().len, None);
49 assert!(s.as_seek().is_none());
50
51 let mut buf = Vec::new();
52 s.read_to_end(&mut buf).unwrap();
53 assert_eq!(buf, b"hello");
54 }
55
56 #[test]
57 fn file_source_is_seekable_and_knows_its_length() {
58 let mut f = tempfile::NamedTempFile::new().unwrap();
59 std::io::Write::write_all(&mut f, b"0123456789").unwrap();
60 let mut s = FileSource::open(f.path()).unwrap();
61
62 assert!(s.caps().seekable);
63 assert_eq!(s.caps().len, Some(10));
64
65 let seek = s.as_seek().expect("file must expose seek");
66 seek.seek(std::io::SeekFrom::Start(4)).unwrap();
67 let mut buf = [0u8; 3];
68 seek.read_exact(&mut buf).unwrap();
69 assert_eq!(&buf, b"456");
70 }
71
72 #[test]
73 fn peek_source_replays_the_prefix_it_consumed() {
74 let inner: Box<dyn Source> = Box::new(ReaderSource::new(std::io::Cursor::new(
76 b"MAGIC-then-payload".to_vec(),
77 )));
78 let mut p = PeekSource::fill(inner, 5).unwrap();
79 assert_eq!(p.prefix(), b"MAGIC");
80
81 let mut all = Vec::new();
82 p.read_to_end(&mut all).unwrap();
83 assert_eq!(all, b"MAGIC-then-payload");
84 }
85
86 #[test]
87 fn peek_source_tolerates_input_shorter_than_the_request() {
88 let inner: Box<dyn Source> =
89 Box::new(ReaderSource::new(std::io::Cursor::new(b"ab".to_vec())));
90 let mut p = PeekSource::fill(inner, 4096).unwrap();
91 assert_eq!(p.prefix(), b"ab");
92
93 let mut all = Vec::new();
94 p.read_to_end(&mut all).unwrap();
95 assert_eq!(all, b"ab");
96 }
97}