kxio 1.1.2

Provides injectable Filesystem and Network resources to make code more testable
Documentation
use std::path::{Path, PathBuf};

pub const fn new(base: PathBuf) -> RealFileSystem {
    RealFileSystem { base }
}

#[derive(Clone, Debug)]
pub struct RealFileSystem {
    base: PathBuf,
}

impl super::FileSystemLike for RealFileSystem {
    fn base(&self) -> &Path {
        &self.base
    }

    fn dir_create(&self, path: &Path) -> super::Result<()> {
        self.validate(path)?;
        std::fs::create_dir(path).map_err(Into::into)
    }

    fn dir_create_all(&self, path: &Path) -> super::Result<()> {
        self.validate(path)?;
        std::fs::create_dir_all(path).map_err(Into::into)
    }

    fn file_read_to_string(&self, path: &Path) -> super::Result<String> {
        self.validate(path)?;
        std::fs::read_to_string(path).map_err(Into::into)
    }

    fn file_write(&self, path: &Path, contents: &str) -> super::Result<()> {
        self.validate(path)?;
        std::fs::write(path, contents).map_err(Into::into)
    }

    fn path_exists(&self, path: &Path) -> super::Result<bool> {
        self.validate(path)?;
        Ok(path.exists())
    }

    fn path_is_dir(&self, path: &Path) -> super::Result<bool> {
        self.validate(path)?;
        Ok(path.is_dir())
    }

    fn path_is_file(&self, path: &Path) -> super::Result<bool> {
        self.validate(path)?;
        Ok(path.is_file())
    }

    fn path_of(&self, path: PathBuf) -> super::Result<PathBuf> {
        let path_of = self.base.as_path().join(path);
        self.validate(&path_of)?;
        Ok(path_of)
    }
}

impl RealFileSystem {
    fn validate(&self, path: &Path) -> super::Result<()> {
        let path = self.clean_path(path)?;
        if !path.starts_with(&self.base) {
            return Err(super::Error::PathTraversal {
                base: self.base.clone(),
                path,
            });
        }
        Ok(())
    }

    fn clean_path(&self, path: &Path) -> super::Result<PathBuf> {
        // let path = path.as_ref();
        use path_clean::PathClean;
        let abs_path = if path.is_absolute() {
            path.to_path_buf()
        } else {
            std::env::current_dir()?.join(path)
        }
        .clean();
        Ok(abs_path)
    }
}