use std::{
io::ErrorKind,
path::{Path, PathBuf},
time::SystemTime,
};
use async_trait::async_trait;
use tokio::fs;
use super::{Driver, DriverError, DriverResult};
use crate::contents::Contents;
pub struct Config {
pub location: PathBuf,
}
#[derive(Clone)]
#[allow(clippy::module_name_repetitions)]
pub struct DiskDriver {
location: PathBuf,
}
impl From<ErrorKind> for DriverError {
fn from(kind: ErrorKind) -> Self {
match kind {
ErrorKind::NotFound => Self::ResourceNotFound,
_ => kind.into(),
}
}
}
impl DiskDriver {
pub async fn new(config: Config) -> DriverResult<Self> {
if !config.location.exists() {
if let Err(err) = fs::create_dir_all(&config.location).await {
return Err(err.kind().into());
}
}
Ok(Self {
location: config.location,
})
}
}
#[async_trait]
impl Driver for DiskDriver {
async fn read(&self, path: &Path) -> DriverResult<Vec<u8>> {
let path = self.location.join(path);
let content = match fs::read(path).await {
Ok(content) => content,
Err(err) => return Err(err.kind().into()),
};
Ok(Contents::from(content).into())
}
async fn file_exists(&self, path: &Path) -> DriverResult<bool> {
if !path.is_file() {
return Ok(false);
}
Ok(path.exists())
}
async fn write(&self, path: &Path, content: Vec<u8>) -> DriverResult<()> {
let path = self.location.join(path);
if let Some(parent) = path.parent() {
if !parent.exists() {
if let Err(err) = fs::create_dir_all(parent).await {
return Err(err.kind().into());
}
}
}
match fs::write(path, content).await {
Ok(()) => Ok(()),
Err(err) => Err(err.kind().into()),
}
}
async fn delete(&self, path: &Path) -> DriverResult<()> {
let path = self.location.join(path);
if !path.exists() {
return Err(DriverError::ResourceNotFound);
};
match fs::remove_file(path).await {
Ok(()) => Ok(()),
Err(err) => Err(err.kind().into()),
}
}
async fn delete_directory(&self, path: &Path) -> DriverResult<()> {
let path = self.location.join(path);
if !path.exists() {
return Err(DriverError::ResourceNotFound);
};
match fs::remove_dir_all(path).await {
Ok(()) => Ok(()),
Err(err) => Err(err.kind().into()),
}
}
async fn last_modified(&self, path: &Path) -> DriverResult<SystemTime> {
let path = self.location.join(path);
if !path.exists() {
return Err(DriverError::ResourceNotFound);
}
let metadata = match fs::metadata(path).await {
Ok(metadata) => metadata,
Err(err) => return Err(err.kind().into()),
};
match metadata.modified() {
Ok(modified) => Ok(modified),
Err(err) => Err(err.kind().into()),
}
}
}