feat(fs): new fs module to replace filesystem
This commit is contained in:
parent
36070f59cc
commit
2e3827e0e0
9 changed files with 182 additions and 20 deletions
|
@ -31,6 +31,9 @@ thiserror = "1.0"
|
||||||
# fs
|
# fs
|
||||||
tempfile = "3.10"
|
tempfile = "3.10"
|
||||||
|
|
||||||
|
# error handling
|
||||||
|
derive_more = { version = "1.0.0-beta.6", features = ["from", "display"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
# testing
|
# testing
|
||||||
assert2 = "0.3"
|
assert2 = "0.3"
|
||||||
|
|
39
README.md
39
README.md
|
@ -13,26 +13,25 @@ There are two FileSystem implementation: [filesystem] and [fs].
|
||||||
|
|
||||||
#### std::fs alternatives
|
#### std::fs alternatives
|
||||||
|
|
||||||
| To Do | [std::fs] | [kxio::fs] | |
|
| To Do | [std::fs] | [kxio::fs::FileSystem] | |
|
||||||
| ----- | ---------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
| ----- | ---------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| [ ] | canonicalize | path::canonicalize | Returns the canonical, absolute form of a path with all intermediate components normalized and symbolic links resolved. |
|
| [ ] | canonicalize | path_canonicalize | Returns the canonical, absolute form of a path with all intermediate components normalized and symbolic links resolved. |
|
||||||
| [ ] | copy | file::copy | Copies the contents of one file to another. This function will also copy the permission bits of the original file to the destination file. |
|
| [ ] | copy | file_copy | Copies the contents of one file to another. This function will also copy the permission bits of the original file to the destination file. |
|
||||||
| [ ] | create_dir | dir::create | Creates a new, empty directory at the provided path |
|
| [ ] | create_dir | dir_create | Creates a new, empty directory at the provided path |
|
||||||
| [ ] | create_dir_all | dir::create_all | Recursively create a directory and all of its parent components if they are missing. |
|
| [ ] | create_dir_all | dir_create_all | Recursively create a directory and all of its parent components if they are missing. |
|
||||||
| [ ] | hard_link | link::create | Creates a new hard link on the filesystem. |
|
| [ ] | hard_link | link_create | Creates a new hard link on the filesystem. |
|
||||||
| [ ] | metadata | path::metadata | Given a path, query the file system to get information about a file, directory, etc. |
|
| [ ] | metadata | path_metadata | Given a path, query the file system to get information about a file, directory, etc. |
|
||||||
| [ ] | read | file::read | Read the entire contents of a file into a bytes vector. |
|
| [ ] | read | file_read | Read the entire contents of a file into a bytes vector. |
|
||||||
| [ ] | read_dir | dir::read | Returns an iterator over the entries within a directory. |
|
| [ ] | read_dir | dir_read | Returns an iterator over the entries within a directory. |
|
||||||
| [ ] | read_link | link::read | Reads a symbolic link, returning the file that the link points to. |
|
| [ ] | read_link | link_read | Reads a symbolic link, returning the file that the link points to. |
|
||||||
| [ ] | read_to_string | file::read_to_string | Read the entire contents of a file into a string. |
|
| [x] | read_to_string | file_read_to_string | Read the entire contents of a file into a string. |
|
||||||
| [ ] | remove_dir | dir::remove | Removes an empty directory. |
|
| [ ] | remove_dir | dir_remove | Removes an empty directory. |
|
||||||
| [ ] | remove_dir_all | dir::remove_all | Removes a directory at this path, after removing all its contents. Use carefully! |
|
| [ ] | remove_dir_all | dir_remove_all | Removes a directory at this path, after removing all its contents. Use carefully! |
|
||||||
| [ ] | remove_file | file::remove | Removes a file from the filesystem. |
|
| [ ] | remove_file | file_remove | Removes a file from the filesystem. |
|
||||||
| [ ] | rename | path::rename | Rename a file or directory to a new name, replacing the original file if to already exists. |
|
| [ ] | rename | path_rename | Rename a file or directory to a new name, replacing the original file if to already exists. |
|
||||||
| [ ] | set_permissions | path::set_permissions | Changes the permissions found on a file or a directory. |
|
| [ ] | set_permissions | path_set_permissions | Changes the permissions found on a file or a directory. |
|
||||||
| [ ] | symlink_metadata | link::metadata | Query the metadata about a file without following symlinks. |
|
| [ ] | symlink_metadata | link_metadata | Query the metadata about a file without following symlinks. |
|
||||||
| [ ] | write | file::write | Write a slice as the entire contents of a file. |
|
| [x] | write | file_write | Write a slice as the entire contents of a file. |
|
||||||
| [ ] | try_exists | path::exists | Returns Ok(true) if the path points at an existing entity. |
|
|
||||||
|
|
||||||
### Network
|
### Network
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![allow(deprecated)]
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
|
@ -16,6 +18,7 @@ pub fn temp() -> std::io::Result<FileSystem> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
#[deprecated(since = "1.1.0", note = "Use [kxio::fs::FileSystem] instead")]
|
||||||
pub enum FileSystem {
|
pub enum FileSystem {
|
||||||
Real(RealFileSystem),
|
Real(RealFileSystem),
|
||||||
Temp(TempFileSystem),
|
Temp(TempFileSystem),
|
||||||
|
|
50
src/fs/mod.rs
Normal file
50
src/fs/mod.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use derive_more::From;
|
||||||
|
|
||||||
|
mod real;
|
||||||
|
mod temp;
|
||||||
|
|
||||||
|
#[derive(Debug, From, derive_more::Display)]
|
||||||
|
pub enum Error {
|
||||||
|
Io(std::io::Error),
|
||||||
|
|
||||||
|
#[display("Path access attempted outside of base ({base:?}): {path:?}")]
|
||||||
|
PathTraversal {
|
||||||
|
base: PathBuf,
|
||||||
|
path: PathBuf,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
pub type Result<T> = core::result::Result<T, Error>;
|
||||||
|
|
||||||
|
pub const fn new(base: PathBuf) -> FileSystem {
|
||||||
|
FileSystem::Real(real::new(base))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn temp() -> Result<FileSystem> {
|
||||||
|
temp::new().map(FileSystem::Temp)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum FileSystem {
|
||||||
|
Real(real::RealFileSystem),
|
||||||
|
Temp(temp::TempFileSystem),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for FileSystem {
|
||||||
|
type Target = dyn FileSystemLike;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
match self {
|
||||||
|
Self::Real(fs) => fs,
|
||||||
|
Self::Temp(fs) => fs.deref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub trait FileSystemLike {
|
||||||
|
fn base(&self) -> &Path;
|
||||||
|
fn path_of(&self, path: PathBuf) -> Result<PathBuf>;
|
||||||
|
fn file_write(&self, path: &Path, contents: &str) -> Result<()>;
|
||||||
|
fn file_read_to_string(&self, path: &Path) -> Result<String>;
|
||||||
|
fn path_exists(&self, path: &Path) -> Result<bool>;
|
||||||
|
fn path_is_file(&self, path: &Path) -> Result<bool>;
|
||||||
|
}
|
52
src/fs/real.rs
Normal file
52
src/fs/real.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
pub const fn new(base: PathBuf) -> RealFileSystem {
|
||||||
|
RealFileSystem { base }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RealFileSystem {
|
||||||
|
base: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::FileSystemLike for RealFileSystem {
|
||||||
|
fn base(&self) -> &Path {
|
||||||
|
&self.base
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn file_write(&self, path: &Path, contents: &str) -> super::Result<()> {
|
||||||
|
self.validate(path)?;
|
||||||
|
std::fs::write(path, contents).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 path_is_file(&self, path: &Path) -> super::Result<bool> {
|
||||||
|
self.validate(path)?;
|
||||||
|
Ok(path.is_file())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path_exists(&self, path: &Path) -> super::Result<bool> {
|
||||||
|
self.validate(path)?;
|
||||||
|
Ok(path.exists())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RealFileSystem {
|
||||||
|
fn validate(&self, path: &std::path::Path) -> super::Result<()> {
|
||||||
|
if !path.starts_with(&self.base) {
|
||||||
|
return Err(super::Error::PathTraversal {
|
||||||
|
base: self.base.clone(),
|
||||||
|
path: path.to_path_buf(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
28
src/fs/temp.rs
Normal file
28
src/fs/temp.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use tempfile::TempDir;
|
||||||
|
|
||||||
|
pub(super) fn new() -> super::Result<TempFileSystem> {
|
||||||
|
let temp_dir = tempfile::tempdir()?;
|
||||||
|
let base = temp_dir.path().to_path_buf();
|
||||||
|
let temp_dir = Arc::new(Mutex::new(temp_dir));
|
||||||
|
let real = super::real::new(base);
|
||||||
|
|
||||||
|
Ok(TempFileSystem {
|
||||||
|
real,
|
||||||
|
_temp_dir: temp_dir,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TempFileSystem {
|
||||||
|
real: super::real::RealFileSystem,
|
||||||
|
_temp_dir: Arc<Mutex<TempDir>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for TempFileSystem {
|
||||||
|
type Target = dyn super::FileSystemLike;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.real
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,9 @@
|
||||||
#[cfg(feature = "fs")]
|
#[cfg(feature = "fs")]
|
||||||
pub mod filesystem;
|
pub mod filesystem;
|
||||||
|
|
||||||
|
#[cfg(feature = "fs")]
|
||||||
|
pub mod fs;
|
||||||
|
|
||||||
#[cfg(feature = "network")]
|
#[cfg(feature = "network")]
|
||||||
pub mod network;
|
pub mod network;
|
||||||
|
|
||||||
|
|
20
src/tests/fs.rs
Normal file
20
src/tests/fs.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use crate::fs;
|
||||||
|
|
||||||
|
type TestResult = Result<(), crate::fs::Error>;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn write_read_file_exists() -> TestResult {
|
||||||
|
let temp_fs = fs::temp()?;
|
||||||
|
let name: PathBuf = temp_fs.path_of("foo".into())?;
|
||||||
|
temp_fs.file_write(&name, "content")?;
|
||||||
|
let c = temp_fs.file_read_to_string(&name)?;
|
||||||
|
assert_eq!(c, "content");
|
||||||
|
let exists = temp_fs.path_exists(&name)?;
|
||||||
|
assert!(exists);
|
||||||
|
let is_file = temp_fs.path_is_file(&name)?;
|
||||||
|
assert!(is_file);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -1,5 +1,8 @@
|
||||||
#[cfg(feature = "fs")]
|
#[cfg(feature = "fs")]
|
||||||
pub mod filesystem;
|
pub mod filesystem;
|
||||||
|
|
||||||
|
#[cfg(feature = "fs")]
|
||||||
|
pub mod fs;
|
||||||
|
|
||||||
// #[cfg(feature = "network")]
|
// #[cfg(feature = "network")]
|
||||||
// pub mod network;
|
// pub mod network;
|
||||||
|
|
Loading…
Reference in a new issue