164 lines
4.8 KiB
Rust
164 lines
4.8 KiB
Rust
use std::fs;
|
|
use std::io::{ErrorKind, Read, Write};
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use serde::Serialize;
|
|
use serde::de::DeserializeOwned;
|
|
|
|
use super::{FileSystem, FsError, LegacyFileSystem};
|
|
|
|
pub struct Fs {
|
|
project_root: PathBuf,
|
|
}
|
|
|
|
impl Fs {
|
|
/// Resolve the user supplied *relative* path against the project root.
|
|
#[inline]
|
|
fn full_path(&self, path: &Path) -> PathBuf {
|
|
self.project_root.join(path)
|
|
}
|
|
|
|
pub fn new(root: impl Into<PathBuf>) -> Self {
|
|
Self {
|
|
project_root: root.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FileSystem for Fs {
|
|
fn read(&self, path: &Path) -> Result<Vec<u8>, FsError> {
|
|
fs::read(self.full_path(path)).map_err(FsError::Io)
|
|
}
|
|
|
|
fn write(&self, path: &Path, data: &[u8]) -> Result<(), FsError> {
|
|
fs::File::create(self.full_path(path))?
|
|
.write(data)
|
|
.map_err(FsError::Io)?;
|
|
Ok(())
|
|
}
|
|
|
|
fn delete(&self, path: &Path) -> Result<(), FsError> {
|
|
let full_path = self.full_path(path);
|
|
match fs::remove_file(&full_path) {
|
|
Ok(()) => Ok(()),
|
|
Err(e) if e.kind() == ErrorKind::IsADirectory => {
|
|
// Remove a directory tree.
|
|
fs::remove_dir_all(&full_path).map_err(FsError::Io)
|
|
}
|
|
Err(e) => Err(FsError::Io(e)),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct NativeFileSystem {
|
|
project_root: PathBuf,
|
|
}
|
|
|
|
impl NativeFileSystem {
|
|
/// Create a new instance.
|
|
pub fn new(root: impl Into<PathBuf>) -> Self {
|
|
Self {
|
|
project_root: root.into(),
|
|
}
|
|
}
|
|
|
|
/// Resolve the user supplied *relative* path against the project root.
|
|
#[inline]
|
|
fn full_path(&self, path: &Path) -> PathBuf {
|
|
self.project_root.join(path)
|
|
}
|
|
}
|
|
|
|
impl LegacyFileSystem for NativeFileSystem {
|
|
fn read<T: DeserializeOwned>(&self, path: &Path) -> Result<T, FsError> {
|
|
let full_path = self.full_path(path);
|
|
let content = fs::read_to_string(full_path).map_err(FsError::Io)?;
|
|
ron::from_str(&content).map_err(FsError::Spanned)
|
|
}
|
|
|
|
fn read_bytes(&self, path: &Path) -> Result<Vec<u8>, FsError> {
|
|
let full_path = self.full_path(path);
|
|
let mut contents = Vec::new();
|
|
fs::File::open(full_path)?.read_to_end(&mut contents)?;
|
|
Ok(contents)
|
|
}
|
|
|
|
fn write<T: Serialize>(&self, path: &Path, data: T) -> Result<(), FsError> {
|
|
let full_path = self.full_path(path);
|
|
|
|
// Ensure the parent directory exists.
|
|
if let Some(parent) = full_path.parent() {
|
|
fs::create_dir_all(parent)?;
|
|
}
|
|
|
|
let content = ron::to_string(&data).map_err(FsError::Ron)?;
|
|
fs::File::create(full_path)?
|
|
.write(content.as_bytes())
|
|
.map_err(FsError::Io)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn delete(&self, path: &Path) -> Result<(), FsError> {
|
|
let full_path = self.full_path(path);
|
|
match fs::remove_file(&full_path) {
|
|
Ok(()) => Ok(()),
|
|
Err(e) if e.kind() == ErrorKind::IsADirectory => {
|
|
// Remove a directory tree.
|
|
fs::remove_dir_all(full_path).map_err(FsError::Io)
|
|
}
|
|
Err(e) => Err(FsError::Io(e)),
|
|
}
|
|
}
|
|
|
|
fn mkdir(&self, path: &Path) -> Result<(), FsError> {
|
|
let full_path = self.full_path(path);
|
|
fs::create_dir_all(full_path).map_err(FsError::Io)
|
|
}
|
|
|
|
fn lsfiles(&self, path: &Path) -> Result<Vec<PathBuf>, FsError> {
|
|
let full_path = self.full_path(path);
|
|
let paths = fs::read_dir(full_path)?
|
|
.filter_map(|res| res.ok())
|
|
.filter(|entry| entry.file_type().unwrap().is_file())
|
|
.map(|entry| entry.path())
|
|
.collect();
|
|
|
|
Ok(paths)
|
|
}
|
|
|
|
fn lsdirs(&self, path: &Path) -> Result<Vec<PathBuf>, FsError> {
|
|
let full_path = self.full_path(path);
|
|
let paths = fs::read_dir(full_path)?
|
|
.filter_map(|res| res.ok())
|
|
.filter(|entry| entry.file_type().unwrap().is_dir())
|
|
.map(|entry| entry.path())
|
|
.collect();
|
|
|
|
Ok(paths)
|
|
}
|
|
|
|
fn exists(&self, path: &Path) -> bool {
|
|
let full_path = self.full_path(path);
|
|
full_path.exists()
|
|
}
|
|
|
|
fn rename(&self, path: &Path, other: &Path) -> Result<(), FsError> {
|
|
let full_path = self.full_path(path);
|
|
let full_other = self.full_path(other);
|
|
fs::rename(full_path, full_other).map_err(FsError::Io)
|
|
}
|
|
|
|
fn config_path(&self) -> PathBuf {
|
|
match std::env::var("HOME") {
|
|
Ok(path) => PathBuf::from(path + "/.config/worldcoder/settings.ron"),
|
|
Err(_) => {
|
|
eprintln!(
|
|
"XDG_CONFIG_HOME not set, using default path of ~/.config/worldcoder/settings.ron"
|
|
);
|
|
"~/.config/worldcoder/settings.ron".into()
|
|
}
|
|
}
|
|
}
|
|
}
|