use std::io;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use crate::env::{Capabilities, DirEntry, FileMeta, ReadFile, WriteFile, WriteMode};
use super::mirror::MirrorFs;
use super::pool::SahPool;
pub(super) trait OpfsStore: Send + Sync + std::fmt::Debug {
fn create_dir_all(&self, path: &Path) -> io::Result<()>;
fn read_dir(&self, path: &Path) -> io::Result<Vec<DirEntry>>;
fn open_read(&self, path: &Path) -> io::Result<Box<dyn ReadFile>>;
fn open_write(&self, path: &Path, mode: WriteMode) -> io::Result<Box<dyn WriteFile>>;
fn metadata(&self, path: &Path) -> io::Result<FileMeta>;
fn remove_file(&self, path: &Path) -> io::Result<()>;
fn rename(&self, from: &Path, to: &Path) -> io::Result<()>;
fn exists(&self, path: &Path) -> bool;
fn sync_dir(&self, path: &Path) -> io::Result<()>;
fn capabilities(&self) -> Capabilities;
}
fn entries(raw: Vec<(PathBuf, bool)>) -> Vec<DirEntry> {
raw.into_iter()
.map(|(path, is_dir)| DirEntry { path, is_dir })
.collect()
}
fn short_read(want: usize, got: usize) -> io::Error {
io::Error::new(
io::ErrorKind::UnexpectedEof,
format!("read {got} of {want} bytes"),
)
}
#[derive(Debug)]
pub(super) struct PoolStore(pub(super) Arc<SahPool>);
impl OpfsStore for PoolStore {
fn create_dir_all(&self, path: &Path) -> io::Result<()> {
self.0.create_dir_all(path)
}
fn read_dir(&self, path: &Path) -> io::Result<Vec<DirEntry>> {
self.0.read_dir(path).map(entries)
}
fn open_read(&self, path: &Path) -> io::Result<Box<dyn ReadFile>> {
let (slot, generation) = self.0.open_read(path)?;
Ok(Box::new(PoolReader {
pool: Arc::clone(&self.0),
slot,
generation,
}))
}
fn open_write(&self, path: &Path, mode: WriteMode) -> io::Result<Box<dyn WriteFile>> {
let (slot, generation, end) = self
.0
.open_write(path, !matches!(mode, WriteMode::Truncate))?;
Ok(Box::new(PoolWriter {
pool: Arc::clone(&self.0),
slot,
generation,
offset: match mode {
WriteMode::Append => end,
WriteMode::Truncate | WriteMode::Update => 0,
},
}))
}
fn metadata(&self, path: &Path) -> io::Result<FileMeta> {
let (len, is_dir) = self.0.metadata(path)?;
Ok(FileMeta { len, is_dir })
}
fn remove_file(&self, path: &Path) -> io::Result<()> {
self.0.remove_file(path)
}
fn rename(&self, from: &Path, to: &Path) -> io::Result<()> {
self.0.rename(from, to)
}
fn exists(&self, path: &Path) -> bool {
self.0.exists(path)
}
fn sync_dir(&self, path: &Path) -> io::Result<()> {
self.0.sync_dir(path)
}
fn capabilities(&self) -> Capabilities {
Capabilities::none()
.with_atomic_rename(true)
.with_sync_dir(true)
.with_durable_sync(true)
}
}
struct PoolReader {
pool: Arc<SahPool>,
slot: usize,
generation: u64,
}
impl ReadFile for PoolReader {
fn read_exact_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> {
let got = self.pool.read_at(self.slot, self.generation, offset, buf)?;
if got != buf.len() {
return Err(short_read(buf.len(), got));
}
Ok(())
}
fn len(&self) -> io::Result<u64> {
self.pool.file_len(self.slot, self.generation)
}
}
struct PoolWriter {
pool: Arc<SahPool>,
slot: usize,
generation: u64,
offset: u64,
}
impl WriteFile for PoolWriter {
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
if buf.is_empty() {
return Ok(());
}
self.pool
.write_at(self.slot, self.generation, self.offset, buf)?;
self.offset += buf.len() as u64;
Ok(())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
fn sync_all(&mut self) -> io::Result<()> {
self.pool.sync(self.slot, self.generation)
}
fn set_len(&mut self, len: u64) -> io::Result<()> {
self.pool.set_len(self.slot, self.generation, len)?;
self.offset = self.offset.min(len);
Ok(())
}
fn len(&self) -> io::Result<u64> {
self.pool.file_len(self.slot, self.generation)
}
}
#[derive(Debug)]
pub(super) struct MirrorStore(pub(super) Arc<MirrorFs>);
impl OpfsStore for MirrorStore {
fn create_dir_all(&self, path: &Path) -> io::Result<()> {
self.0.create_dir_all(path)
}
fn read_dir(&self, path: &Path) -> io::Result<Vec<DirEntry>> {
self.0.read_dir(path).map(entries)
}
fn open_read(&self, path: &Path) -> io::Result<Box<dyn ReadFile>> {
self.0.file_len(path)?;
Ok(Box::new(MirrorReader {
fs: Arc::clone(&self.0),
path: path.to_path_buf(),
}))
}
fn open_write(&self, path: &Path, mode: WriteMode) -> io::Result<Box<dyn WriteFile>> {
let offset = self.0.create(path, matches!(mode, WriteMode::Truncate))?;
Ok(Box::new(MirrorWriter {
fs: Arc::clone(&self.0),
path: path.to_path_buf(),
offset: match mode {
WriteMode::Append => offset,
WriteMode::Truncate | WriteMode::Update => 0,
},
}))
}
fn metadata(&self, path: &Path) -> io::Result<FileMeta> {
let (len, is_dir) = self.0.metadata(path)?;
Ok(FileMeta { len, is_dir })
}
fn remove_file(&self, path: &Path) -> io::Result<()> {
self.0.remove_file(path)
}
fn rename(&self, from: &Path, to: &Path) -> io::Result<()> {
self.0.rename(from, to)
}
fn exists(&self, path: &Path) -> bool {
self.0.exists(path)
}
fn sync_dir(&self, _path: &Path) -> io::Result<()> {
Ok(())
}
fn capabilities(&self) -> Capabilities {
Capabilities::none().with_atomic_rename(true)
}
}
struct MirrorReader {
fs: Arc<MirrorFs>,
path: PathBuf,
}
impl ReadFile for MirrorReader {
fn read_exact_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> {
let got = self.fs.read_at(&self.path, offset, buf)?;
if got != buf.len() {
return Err(short_read(buf.len(), got));
}
Ok(())
}
fn len(&self) -> io::Result<u64> {
self.fs.file_len(&self.path)
}
}
struct MirrorWriter {
fs: Arc<MirrorFs>,
path: PathBuf,
offset: u64,
}
impl WriteFile for MirrorWriter {
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
if buf.is_empty() {
return Ok(());
}
self.fs.write_at(&self.path, self.offset, buf)?;
self.offset += buf.len() as u64;
Ok(())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
fn sync_all(&mut self) -> io::Result<()> {
Ok(())
}
fn set_len(&mut self, len: u64) -> io::Result<()> {
self.fs.set_len(&self.path, len)?;
self.offset = self.offset.min(len);
Ok(())
}
fn len(&self) -> io::Result<u64> {
self.fs.file_len(&self.path)
}
}