1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
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;
/// Configuration parameters for initializing a `DiskDriver`.
pub struct Config {
pub location: PathBuf,
}
/// The `DiskDriver` struct represents a disk-based implementation of the
/// `Driver` trait.
///
/// It provides methods for interacting with files and directories on the disk.
#[derive(Clone)]
#[allow(clippy::module_name_repetitions)]
pub struct DiskDriver {
/// The location on the disk where the `DiskDriver` will operate.
location: PathBuf,
}
impl From<ErrorKind> for DriverError {
fn from(kind: ErrorKind) -> Self {
match kind {
ErrorKind::NotFound => Self::ResourceNotFound,
_ => kind.into(),
}
}
}
impl DiskDriver {
/// Initializes a new `DiskDriver` instance with the specified
/// configuration.
///
/// If the specified location does not exist, it creates the necessary
/// directories.
///
/// # Errors
///
/// Returns an error if the initialization fails, such as being unable to
/// create the required directories.
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 {
/// Reads the contents of a file at the specified path within the disk-based
/// storage.
///
/// # Errors
///
/// Returns an error if there is an issue reading from the file or decoding
/// its contents.
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())
}
/// Checks if a file exists at the specified path within the disk-based
/// storage.
///
/// If the path does not point to a file, the method returns `Ok(false)`.
/// Otherwise, it checks if the file exists and returns the result.
///
/// # Errors
///
/// Returns an error if there is an issue checking the existence of the
/// file.
async fn file_exists(&self, path: &Path) -> DriverResult<bool> {
if !path.is_file() {
return Ok(false);
}
Ok(path.exists())
}
/// Writes the provided content to a file at the specified path within the
/// disk-based storage.
///
/// If the directory structure leading to the file does not exist, it
/// creates the necessary directories.
///
/// # Errors
///
/// Returns an error if there is any issue creating directories, writing to
/// the file, or handling other I/O-related errors.
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()),
}
}
/// Deletes the file at the specified path within the disk-based storage.
///
/// # Errors
///
/// Returns an error if the file does not exist or if there is any issue
/// deleting the file.
///
/// If the file does not exist, the error variant
/// `DriverError::ResourceNotFound` is returned.
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()),
}
}
/// Deletes the directory and its contents at the specified path within the
/// disk-based storage.
///
/// # Errors
///
/// Returns an error if the directory does not exist or if there is any
/// issue deleting the directory.
///
/// If the directory does not exist, the error variant
/// `DriverError::DirectoryNotFound` is returned.
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()),
}
}
/// Retrieves the last modification time of the file at the specified path
/// within the disk-based storage. # Errors
///
/// Returns an error if the file does not exist or if there is any issue
/// retrieving the last modification time.
///
/// If the file does not exist, the error variant
/// `DriverError::ResourceNotFound` is returned.
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()),
}
}
}