dyn_fs 0.0.7

Abstraction for the std::fs stdlib
Documentation
use std::collections::HashMap;
use std::io;
use std::io::Cursor;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::RwLock;
use std::sync::RwLockReadGuard;
use std::sync::RwLockWriteGuard;

use normalize_path::NormalizePath;

use super::MemoryMetadata;
use crate::file_system::File;
use crate::file_system::FileSystem;
use crate::file_system::Metadata;

#[derive(Debug)]
enum Entry {
  File(Vec<u8>),
  Directory,
}

#[derive(Default, Debug, Clone)]
pub struct MemoryFileSystem {
  files: Arc<RwLock<HashMap<PathBuf, Entry>>>,
}

impl MemoryFileSystem {
  fn files_write<'a>(
    &'a self
  ) -> io::Result<RwLockWriteGuard<'a, HashMap<PathBuf, Entry>>> {
    let Ok(files) = self.files.write() else {
      return Err(io::Error::other("Unable to unlock mutex"));
    };
    Ok(files)
  }

  fn files_read<'a>(
    &'a self
  ) -> io::Result<RwLockReadGuard<'a, HashMap<PathBuf, Entry>>> {
    let Ok(files) = self.files.read() else {
      return Err(io::Error::other("Unable to unlock mutex"));
    };
    Ok(files)
  }
}

impl FileSystem for MemoryFileSystem {
  fn open(
    &self,
    path: &Path,
  ) -> io::Result<Box<dyn File>> {
    Ok(Box::new(Cursor::new(self.read(path)?)))
  }

  fn read(
    &self,
    path: &Path,
  ) -> io::Result<Vec<u8>> {
    let path = path.normalize();

    let files = self.files_read()?;

    let Some(file) = files.get(&path) else {
      return Err(std::io::Error::new(
        std::io::ErrorKind::NotFound,
        "File not found",
      ));
    };

    match file {
      Entry::File(contents) => Ok(contents.clone()),
      Entry::Directory => Err(std::io::Error::new(
        std::io::ErrorKind::InvalidInput,
        "Path is a directory",
      )),
    }
  }

  fn read_to_string(
    &self,
    path: &Path,
  ) -> io::Result<String> {
    let path = path.normalize();
    let bytes = self.read(&path)?;
    String::from_utf8(bytes).map_err(|_| std::io::Error::other("Unable to read file as string"))
  }

  fn metadata(
    &self,
    path: &Path,
  ) -> io::Result<Box<dyn Metadata>> {
    let path = path.normalize();
    let files = self.files_read()?;
    let file = files.get(&path);

    Ok(Box::new(MemoryMetadata {
      inner_is_dir: matches!(file, Some(Entry::Directory { .. })),
      inner_is_file: matches!(file, Some(Entry::File { .. })),
    }))
  }

  fn write(
    &self,
    path: &Path,
    contents: &[u8],
  ) -> io::Result<()> {
    let path = path.normalize();

    let mut files = self.files_write()?;
    files.insert(
      path.clone(),
      Entry::File(contents.to_vec()),
    );

    let mut dir = path.parent();
    while let Some(path) = dir {
      files.insert(path.to_path_buf(), Entry::Directory);
      dir = path.parent();
    }

    Ok(())
  }

  fn canonicalize(
    &self,
    _path: &Path,
  ) -> io::Result<PathBuf> {
    todo!()
  }

  fn read_link(
    &self,
    _path: &Path,
  ) -> io::Result<PathBuf> {
    todo!()
  }

  fn copy(
    &self,
    _from: &Path,
    _to: &Path,
  ) -> io::Result<u64> {
    todo!()
  }

  fn create_dir(
    &self,
    _path: &Path,
  ) -> io::Result<()> {
    todo!()
  }

  fn create_dir_all(
    &self,
    _path: &Path,
  ) -> io::Result<()> {
    todo!()
  }

  fn hard_link(
    &self,
    _original: &Path,
    _link: &Path,
  ) -> io::Result<()> {
    todo!()
  }

  fn read_dir(
    &self,
    _path: &Path,
  ) -> io::Result<std::fs::ReadDir> {
    todo!()
  }

  fn remove_dir(
    &self,
    _path: &Path,
  ) -> io::Result<()> {
    todo!()
  }

  fn remove_dir_all(
    &self,
    _path: &Path,
  ) -> io::Result<()> {
    todo!()
  }

  fn remove_file(
    &self,
    _path: &Path,
  ) -> io::Result<()> {
    todo!()
  }

  fn rename(
    &self,
    _from: &Path,
    _to: &Path,
  ) -> io::Result<()> {
    todo!()
  }

  fn set_permissions(
    &self,
    _path: &Path,
    _perm: &dyn crate::file_system::Permissions,
  ) -> io::Result<()> {
    todo!()
  }

  fn symlink_metadata(
    &self,
    _path: &Path,
  ) -> io::Result<Box<dyn Metadata>> {
    todo!()
  }
}

impl File for Cursor<Vec<u8>> {}