added filesystem abstraction layer and implemented basic filesystem - testing not complete yet, still features missing
Continuous integration / build (push) Has been cancelled
Continuous integration / build (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
// Imports
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
use std::fs;
|
||||
use std::io::{ErrorKind, Read};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
use crate::filesystem::{FsError, LegacyFileSystem};
|
||||
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
// Concrete implementation
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
/// The concrete file‑system. All paths are interpreted relative to
|
||||
/// `project_root`.
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
// Implementation of the trait
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
impl LegacyFileSystem for NativeFileSystem {
|
||||
fn read<T: DeserializeOwned>(&self, path: &Path) -> Result<T, FsError> {
|
||||
let full_path = self.full_path(path);
|
||||
let file = fs::File::open(full_path).map_err(FsError::Io)?;
|
||||
serde_json::from_reader(file).map_err(FsError::Serde)
|
||||
}
|
||||
|
||||
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 file = fs::File::create(full_path)?;
|
||||
serde_json::to_writer(file, &data).map_err(FsError::Serde)
|
||||
}
|
||||
|
||||
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 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.json"),
|
||||
Err(_) => {
|
||||
eprintln!(
|
||||
"XDG_CONFIG_HOME not set, using default path of ~/.config/worldcoder/settings.json"
|
||||
);
|
||||
"~/.config/worldcoder/settings.json".into()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user