use crate::file::{File, Metadata, OpenOptions};
use crate::util::{invalid_input, not_supported};
use enumflags2::{bitflags, BitFlags};
use parking_lot::MutexGuard;
use std::io::{Read, Seek, SeekFrom, Write};
use std::{io, mem};
#[bitflags]
#[derive(Debug, Copy, Clone)]
#[repr(u8)]
pub enum FileMode {
Read,
Write,
}
impl FileMode {
pub fn from_options(open_options: &OpenOptions) -> BitFlags<Self> {
let mut mode = BitFlags::empty();
if open_options.read {
mode.insert(FileMode::Read);
}
if open_options.write {
mode.insert(FileMode::Write);
}
mode
}
}
pub struct FileHandle {
contents: MutexGuard<'static, Vec<u8>>,
_mutex: super::File,
pos: usize,
mode: BitFlags<FileMode>,
}
impl FileHandle {
pub fn new(contents_mutex: super::File, mode: BitFlags<FileMode>) -> Self {
let contents = contents_mutex.lock();
Self {
contents: unsafe { mem::transmute::<MutexGuard<'_, Vec<u8>>, MutexGuard<'_, Vec<u8>>>(contents) },
_mutex: contents_mutex,
pos: 0,
mode,
}
}
pub fn clear(&mut self) {
self.contents.clear()
}
fn remaining_slice(&self) -> &[u8] {
let start_pos = self.pos.min(self.contents.len());
&self.contents[start_pos..]
}
fn check_mode(mode: bool) -> io::Result<()> {
if mode {
Ok(())
} else {
Err(not_supported())
}
}
}
impl Read for FileHandle {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
Self::check_mode(self.mode.contains(FileMode::Read))?;
let mut remaining_slice = self.remaining_slice();
let n = remaining_slice.read(buf)?;
self.pos += n;
Ok(n)
}
}
impl Seek for FileHandle {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
let (base_pos, offset) = match pos {
SeekFrom::Start(n) => {
self.pos = n as usize;
return Ok(n);
}
SeekFrom::Current(n) => (self.pos as u64, n),
SeekFrom::End(n) => (self.contents.len() as u64, n),
};
if let Some(n) = base_pos.checked_add_signed(offset) {
self.pos = n as usize;
Ok(n)
} else {
Err(invalid_input(
"Invalid seek to a negative or overflowing position",
))
}
}
}
impl Write for FileHandle {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
Self::check_mode(self.mode.contains(FileMode::Write))?;
let pos = self.pos.min(self.contents.len());
let needed_len = pos.saturating_add(buf.len());
if needed_len > self.contents.len() {
self.contents.resize(needed_len, 0);
}
self.contents[pos..needed_len].copy_from_slice(buf);
Ok(needed_len - pos)
}
fn flush(&mut self) -> io::Result<()> {
Self::check_mode(self.mode.contains(FileMode::Write))?;
Ok(())
}
}
impl File for FileHandle {
fn metadata(&self) -> crate::Result<Metadata> {
Ok(Metadata::file(self.contents.len() as u64))
}
}