framed_file/
lib.rs

1use std::io::{self, Read, Seek, SeekFrom, Write};
2use std::ops::Range;
3
4#[cfg(not(feature = "shared"))]
5type File = std::fs::File;
6#[cfg(feature = "shared")]
7type File = shared_file::SharedFile;
8
9type Frame = Range<u64>;
10
11pub struct FramedFile {
12    file: File,
13    frame: Frame,
14    current_pos: u64,
15}
16
17impl FramedFile {
18    pub fn new(file: File, frame: Frame) -> io::Result<Self> {
19        Self::from_range(file, frame)
20    }
21
22    pub fn from_range(mut file: File, frame: Frame) -> io::Result<Self> {
23        file.seek(SeekFrom::Start(frame.start))?;
24
25        Ok(Self {
26            file,
27            frame,
28            current_pos: 0,
29        })
30    }
31
32    pub fn from_len(file: File, start: u64, len: u64) -> io::Result<Self> {
33        let end = start + len;
34
35        Self::from_range(file, start..end)
36    }
37
38    pub fn position(&self) -> u64 {
39        self.current_pos
40    }
41
42    pub fn frame(&self) -> &Frame {
43        &self.frame
44    }
45
46    pub fn frame_start(&self) -> u64 {
47        self.frame().start
48    }
49
50    pub fn frame_end(&self) -> u64 {
51        self.frame().end
52    }
53
54    pub fn frame_len(&self) -> u64 {
55        self.frame_end() - self.frame_start()
56    }
57
58    pub fn remaining_len(&self) -> u64 {
59        self.frame_len() - self.position()
60    }
61
62    pub fn into_raw_file(self) -> File {
63        self.file
64    }
65}
66
67impl Read for FramedFile {
68    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
69        let max_to_read = self.remaining_len() as usize;
70        let buf_len = buf.len();
71        let to_read = max_to_read.min(buf_len);
72
73        if to_read == 0 {
74            return Ok(0);
75        }
76
77        let bytes_read = self.file.read(&mut buf[..to_read])?;
78        self.current_pos += bytes_read as u64;
79        Ok(bytes_read)
80    }
81}
82
83impl Write for FramedFile {
84    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
85        let max_to_write = self.remaining_len() as usize;
86        let buf_len = buf.len();
87        let to_write = max_to_write.min(buf_len);
88
89        if to_write == 0 {
90            return Err(io::Error::new(
91                io::ErrorKind::WriteZero,
92                "Write exceeds file range",
93            ));
94        }
95
96        let bytes_written = self.file.write(&buf[..to_write])?;
97        self.current_pos += bytes_written as u64;
98        Ok(bytes_written)
99    }
100
101    fn flush(&mut self) -> io::Result<()> {
102        self.file.flush()
103    }
104}
105
106impl Seek for FramedFile {
107    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
108        let new_pos = match pos {
109            SeekFrom::Start(offset) => offset,
110            SeekFrom::End(offset) => (self.frame_len() as i64 + offset) as u64,
111            SeekFrom::Current(offset) => (self.current_pos as i64 + offset) as u64,
112        };
113
114        if new_pos > self.frame_len() {
115            return Err(io::Error::new(
116                io::ErrorKind::InvalidInput,
117                "Seek position out of bounds",
118            ));
119        }
120
121        self.file
122            .seek(SeekFrom::Start(self.frame_start() + new_pos))?;
123        self.current_pos = new_pos;
124        Ok(self.current_pos)
125    }
126}