Files
worldcoder/src/services/persist/native.rs
T

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()
}
}
}
}