1use std::fs::File;
2use std::io::{self, Seek};
3use std::io::{prelude::*, SeekFrom};
4use std::os::unix::fs::FileExt;
5use std::sync::Arc;
6
7#[derive(Clone)]
8pub struct SharedFile {
9 cur_pos: u64,
10 file: Arc<File>,
11}
12
13impl SharedFile {
14 pub fn new(mut file: File) -> io::Result<Self> {
15 Ok(Self {
16 cur_pos: file.stream_position()?,
17 file: Arc::new(file),
18 })
19 }
20
21 pub fn len(&self) -> io::Result<u64> {
22 Ok(self.file.metadata()?.len())
23 }
24
25 pub fn is_empty(&self) -> io::Result<bool> {
26 self.len().map(|l| l == 0)
27 }
28}
29
30impl Read for SharedFile {
31 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
32 let readed = self.file.read_at(buf, self.cur_pos)?;
33
34 self.cur_pos += readed as u64;
35
36 Ok(readed)
37 }
38}
39
40impl Write for SharedFile {
41 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
42 let writed = self.file.write_at(buf, self.cur_pos)?;
43
44 self.cur_pos += writed as u64;
45
46 Ok(writed)
47 }
48
49 fn flush(&mut self) -> io::Result<()> {
50 self.file.flush()
51 }
52}
53
54impl Seek for SharedFile {
55 fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
56 let file_len = self.len()?;
57
58 let new_pos = match pos {
59 SeekFrom::Start(offset) => offset,
60 SeekFrom::End(offset) => (file_len as i64 - offset) as u64,
61 SeekFrom::Current(offset) => (self.cur_pos as i64 + offset) as u64,
62 };
63
64 if new_pos > file_len {
65 return Err(io::Error::new(
66 io::ErrorKind::InvalidInput,
67 "seek position out of bounds",
68 ));
69 }
70
71 self.cur_pos = new_pos;
72
73 Ok(new_pos)
74 }
75}