filesystem 0.4.3

Real, fake, and mock implementations of file system operations
Documentation
use std::error::Error as StdError;
use std::ffi::OsString;
use std::io::{Error, ErrorKind};
use std::path::{Path, PathBuf};
use std::vec::IntoIter;

use pseudo::Mock;

use FileSystem;

#[derive(Debug, Clone, PartialEq)]
pub struct FakeError {
    kind: ErrorKind,
    description: String,
}

#[derive(Debug, Clone, PartialEq)]
pub struct DirEntry {
    file_name: PathBuf,
    is_file: bool,
}

impl DirEntry {
    pub fn new<P: AsRef<Path>>(file_name: P, is_file: bool) -> Self {
        DirEntry {
            file_name: file_name.as_ref().to_path_buf(),
            is_file,
        }
    }
}

impl ::DirEntry for DirEntry {
    fn path(&self) -> PathBuf {
        self.file_name.clone()
    }

    fn file_name(&self) -> OsString {
        self.file_name.clone().into_os_string()
    }
}

#[derive(Debug)]
pub struct ReadDir(IntoIter<Result<DirEntry, Error>>);

impl ReadDir {
    pub fn new() -> Self {
        ReadDir(vec![].into_iter())
    }
}

impl Default for ReadDir {
    fn default() -> Self {
        Self::new()
    }
}

impl Iterator for ReadDir {
    type Item = Result<DirEntry, Error>;

    fn next(&mut self) -> Option<Self::Item> {
        self.0.next()
    }
}

impl ::ReadDir<DirEntry> for ReadDir {}

impl From<Error> for FakeError {
    fn from(err: Error) -> Self {
        FakeError {
            kind: err.kind(),
            description: err.description().to_string(),
        }
    }
}

impl From<FakeError> for Error {
    fn from(err: FakeError) -> Self {
        Error::new(err.kind, err.description)
    }
}

#[derive(Debug, Clone)]
pub struct MockFileSystem {
    pub current_dir: Mock<(), Result<PathBuf, FakeError>>,
    pub set_current_dir: Mock<PathBuf, Result<(), FakeError>>,

    pub is_dir: Mock<PathBuf, bool>,
    pub is_file: Mock<PathBuf, bool>,

    pub create_dir: Mock<PathBuf, Result<(), FakeError>>,
    pub create_dir_all: Mock<PathBuf, Result<(), FakeError>>,
    pub remove_dir: Mock<PathBuf, Result<(), FakeError>>,
    pub remove_dir_all: Mock<PathBuf, Result<(), FakeError>>,
    pub read_dir: Mock<PathBuf, Result<Vec<Result<DirEntry, FakeError>>, FakeError>>,

    pub write_file: Mock<(PathBuf, Vec<u8>), Result<(), FakeError>>,
    pub overwrite_file: Mock<(PathBuf, Vec<u8>), Result<(), FakeError>>,
    pub read_file: Mock<(PathBuf), Result<Vec<u8>, FakeError>>,
    pub read_file_to_string: Mock<(PathBuf), Result<String, FakeError>>,
    pub create_file: Mock<(PathBuf, Vec<u8>), Result<(), FakeError>>,
    pub remove_file: Mock<(PathBuf), Result<(), FakeError>>,
    pub copy_file: Mock<(PathBuf, PathBuf), Result<(), FakeError>>,

    pub rename: Mock<(PathBuf, PathBuf), Result<(), FakeError>>,

    pub readonly: Mock<(PathBuf), Result<bool, FakeError>>,
    pub set_readonly: Mock<(PathBuf, bool), Result<(), FakeError>>,

    pub len: Mock<(PathBuf), u64>,
}

impl MockFileSystem {
    pub fn new() -> Self {
        MockFileSystem {
            current_dir: Mock::new(Ok(PathBuf::new())),
            set_current_dir: Mock::new(Ok(())),

            is_dir: Mock::new(true),
            is_file: Mock::new(true),

            create_dir: Mock::new(Ok(())),
            create_dir_all: Mock::new(Ok(())),
            remove_dir: Mock::new(Ok(())),
            remove_dir_all: Mock::new(Ok(())),
            read_dir: Mock::new(Ok(vec![])),

            write_file: Mock::new(Ok(())),
            overwrite_file: Mock::new(Ok(())),
            read_file: Mock::new(Ok(vec![])),
            read_file_to_string: Mock::new(Ok(String::new())),
            create_file: Mock::new(Ok(())),
            remove_file: Mock::new(Ok(())),
            copy_file: Mock::new(Ok(())),

            rename: Mock::new(Ok(())),

            readonly: Mock::new(Ok(false)),
            set_readonly: Mock::new(Ok(())),

            len: Mock::new(u64::default()),
        }
    }
}

impl Default for MockFileSystem {
    fn default() -> Self {
        Self::new()
    }
}

impl FileSystem for MockFileSystem {
    type DirEntry = DirEntry;
    type ReadDir = ReadDir;

    fn current_dir(&self) -> Result<PathBuf, Error> {
        self.current_dir.call(()).map_err(Error::from)
    }

    fn set_current_dir<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
        self.set_current_dir
            .call(path.as_ref().to_path_buf())
            .map_err(Error::from)
    }

    fn is_dir<P: AsRef<Path>>(&self, path: P) -> bool {
        self.is_dir.call(path.as_ref().to_path_buf())
    }

    fn is_file<P: AsRef<Path>>(&self, path: P) -> bool {
        self.is_file.call(path.as_ref().to_path_buf())
    }

    fn create_dir<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
        self.create_dir
            .call(path.as_ref().to_path_buf())
            .map_err(Error::from)
    }

    fn create_dir_all<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
        self.create_dir_all
            .call(path.as_ref().to_path_buf())
            .map_err(Error::from)
    }

    fn remove_dir<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
        self.remove_dir
            .call(path.as_ref().to_path_buf())
            .map_err(Error::from)
    }

    fn remove_dir_all<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
        self.remove_dir_all
            .call(path.as_ref().to_path_buf())
            .map_err(Error::from)
    }

    fn read_dir<P: AsRef<Path>>(&self, path: P) -> Result<Self::ReadDir, Error> {
        self.read_dir
            .call(path.as_ref().to_path_buf())
            .map(|entries| {
                let entries: Vec<Result<DirEntry, Error>> = entries
                    .into_iter()
                    .map(|e| e.map_err(Error::from))
                    .collect();

                ReadDir(entries.into_iter())
            })
            .map_err(Error::from)
    }

    fn write_file<P, B>(&self, path: P, buf: B) -> Result<(), Error>
    where
        P: AsRef<Path>,
        B: AsRef<[u8]>,
    {
        self.write_file
            .call((path.as_ref().to_path_buf(), buf.as_ref().to_vec()))
            .map_err(Error::from)
    }

    fn overwrite_file<P, B>(&self, path: P, buf: B) -> Result<(), Error>
    where
        P: AsRef<Path>,
        B: AsRef<[u8]>,
    {
        self.overwrite_file
            .call((path.as_ref().to_path_buf(), buf.as_ref().to_vec()))
            .map_err(Error::from)
    }

    fn read_file<P: AsRef<Path>>(&self, path: P) -> Result<Vec<u8>, Error> {
        self.read_file
            .call(path.as_ref().to_path_buf())
            .map_err(Error::from)
    }

    fn read_file_to_string<P: AsRef<Path>>(&self, path: P) -> Result<String, Error> {
        self.read_file_to_string
            .call(path.as_ref().to_path_buf())
            .map_err(Error::from)
    }

    fn create_file<P, B>(&self, path: P, buf: B) -> Result<(), Error>
    where
        P: AsRef<Path>,
        B: AsRef<[u8]>,
    {
        self.create_file
            .call((path.as_ref().to_path_buf(), buf.as_ref().to_vec()))
            .map_err(Error::from)
    }

    fn remove_file<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
        self.remove_file
            .call(path.as_ref().to_path_buf())
            .map_err(Error::from)
    }

    fn copy_file<P, Q>(&self, from: P, to: Q) -> Result<(), Error>
    where
        P: AsRef<Path>,
        Q: AsRef<Path>,
    {
        self.copy_file
            .call((from.as_ref().to_path_buf(), to.as_ref().to_path_buf()))
            .map_err(Error::from)
    }

    fn rename<P, Q>(&self, from: P, to: Q) -> Result<(), Error>
    where
        P: AsRef<Path>,
        Q: AsRef<Path>,
    {
        self.rename
            .call((from.as_ref().to_path_buf(), to.as_ref().to_path_buf()))
            .map_err(Error::from)
    }

    fn readonly<P: AsRef<Path>>(&self, path: P) -> Result<bool, Error> {
        self.readonly
            .call(path.as_ref().to_path_buf())
            .map_err(Error::from)
    }

    fn set_readonly<P: AsRef<Path>>(&self, path: P, readonly: bool) -> Result<(), Error> {
        self.set_readonly
            .call((path.as_ref().to_path_buf(), readonly))
            .map_err(Error::from)
    }

    fn len<P: AsRef<Path>>(&self, path: P) -> u64 {
        self.len.call(path.as_ref().to_path_buf())
    }
}