modelvault-core 0.15.0

Core engine for ModelVault — application-focused embedded storage with model schemas, validation, and migrations.
Documentation
use std::path::Path;

pub(crate) trait FsOps {
    fn remove_file(&self, path: &Path) -> std::io::Result<()>;
    fn rename(&self, from: &Path, to: &Path) -> std::io::Result<()>;
    fn copy(&self, from: &Path, to: &Path) -> std::io::Result<u64>;

    fn open_read(&self, path: &Path) -> std::io::Result<std::fs::File>;
    #[cfg_attr(not(unix), allow(dead_code))]
    fn open_dir(&self, path: &Path) -> std::io::Result<std::fs::File>;

    fn open_read_write_create_truncate(&self, path: &Path) -> std::io::Result<std::fs::File>;
    fn open_read_write_create_new(&self, path: &Path) -> std::io::Result<std::fs::File>;

    fn read(&self, path: &Path) -> std::io::Result<Vec<u8>>;
    fn write(&self, path: &Path, bytes: &[u8]) -> std::io::Result<()>;
}

pub(crate) struct StdFsOps;

impl FsOps for StdFsOps {
    fn remove_file(&self, path: &Path) -> std::io::Result<()> {
        std::fs::remove_file(path)
    }

    fn rename(&self, from: &Path, to: &Path) -> std::io::Result<()> {
        std::fs::rename(from, to)
    }

    fn copy(&self, from: &Path, to: &Path) -> std::io::Result<u64> {
        std::fs::copy(from, to)
    }

    fn open_read(&self, path: &Path) -> std::io::Result<std::fs::File> {
        std::fs::OpenOptions::new().read(true).open(path)
    }

    #[cfg_attr(not(unix), allow(dead_code))]
    fn open_dir(&self, path: &Path) -> std::io::Result<std::fs::File> {
        std::fs::File::open(path)
    }

    fn open_read_write_create_truncate(&self, path: &Path) -> std::io::Result<std::fs::File> {
        std::fs::OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .truncate(true)
            .open(path)
    }

    fn open_read_write_create_new(&self, path: &Path) -> std::io::Result<std::fs::File> {
        std::fs::OpenOptions::new()
            .read(true)
            .write(true)
            .create_new(true)
            .open(path)
    }

    fn read(&self, path: &Path) -> std::io::Result<Vec<u8>> {
        std::fs::read(path)
    }

    fn write(&self, path: &Path, bytes: &[u8]) -> std::io::Result<()> {
        std::fs::write(path, bytes)
    }
}

#[cfg(test)]
mod std_fs_ops_tests {
    use super::{FsOps, StdFsOps};
    use std::io::Write;

    #[test]
    fn std_fs_ops_delegates_to_std_fs() {
        let tmp = tempfile::tempdir().unwrap();
        let a = tmp.path().join("a.txt");
        let b = tmp.path().join("b.txt");
        let c = tmp.path().join("c.txt");
        let d = tmp.path().join("d.txt");
        let e = tmp.path().join("e.txt");
        let f = tmp.path().join("f.txt");
        let g = tmp.path().join("g.txt");
        let h = tmp.path().join("h.txt");

        StdFsOps.write(&a, b"hi").unwrap();
        assert_eq!(StdFsOps.read(&a).unwrap(), b"hi");
        assert_eq!(StdFsOps.copy(&a, &b).unwrap(), 2);

        let mut w = StdFsOps.open_read_write_create_truncate(&c).unwrap();
        w.write_all(b"ab").unwrap();
        drop(w);
        let mut r = StdFsOps.open_read(&c).unwrap();
        let mut buf = String::new();
        std::io::Read::read_to_string(&mut r, &mut buf).unwrap();
        assert_eq!(buf, "ab");

        StdFsOps.open_read_write_create_new(&d).unwrap();
        assert!(StdFsOps.open_read_write_create_new(&d).is_err());

        std::fs::create_dir_all(tmp.path().join("dir")).unwrap();
        let dir_path = tmp.path().join("dir");
        #[cfg(unix)]
        assert!(StdFsOps.open_dir(&dir_path).is_ok());
        #[cfg(not(unix))]
        let _ = dir_path;

        let mut nw = StdFsOps.open_read_write_create_new(&e).unwrap();
        nw.write_all(b"x").unwrap();
        drop(nw);
        StdFsOps.remove_file(&e).unwrap();
        assert!(!e.exists());

        StdFsOps.write(&f, b"1").unwrap();
        StdFsOps.rename(&f, &g).unwrap();
        assert!(!f.exists());
        assert_eq!(StdFsOps.read(&g).unwrap(), b"1");

        StdFsOps.write(&h, b"").unwrap();
        StdFsOps.remove_file(&h).unwrap();
    }
}