use std::{fs, io, path::{Path, PathBuf}};
use std::io::{BufReader, BufWriter};
#[cfg(feature = "directory_locks")]
use fs2::FileExt;
use uuid::Uuid;
use super::{Result, Storage, StorageError};
#[derive(Debug, Clone)]
pub struct Directory {
path: PathBuf
}
impl Directory {
pub fn new<P>(path: P) -> Result<Self>
where P: AsRef<Path> {
let path: PathBuf = path.as_ref().into();
if !path.exists() {
fs::create_dir_all(&path)?;
}
if path.is_dir() {
Ok(Self {
path
})
} else {
Err(StorageError::other(format!("not a directory: {}", path.display())))
}
}
}
impl Storage for Directory {
type Read = FileReader;
type Write = FileWriter;
type Iterator = Iter;
fn new(&mut self) -> Result<(Uuid, Self::Write)> {
loop {
let entry = Uuid::new_v4();
match FileWriter::new(&self.path, entry) {
Ok(writer) => {
return Ok((entry, writer));
}
Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {
continue;
}
Err(e) => {
return Err(e.into());
}
}
}
}
fn read(&self, entry: Uuid) -> Result<Self::Read> {
match FileReader::read(&self.path, entry) {
Ok(reader) => Ok(reader),
Err(e) if e.kind() == io::ErrorKind::NotFound => Err(StorageError::not_found()),
Err(e) => Err(e.into())
}
}
fn write(&mut self, entry: Uuid) -> Result<Self::Write> {
Ok(FileWriter::write(&self.path, entry)?)
}
fn overwrite(&mut self, entry: Uuid) -> Result<Self::Write> {
match FileWriter::overwrite(&self.path, entry) {
Ok(reader) => Ok(reader),
Err(e) if e.kind() == io::ErrorKind::NotFound => Err(StorageError::not_found()),
Err(e) => Err(e.into())
}
}
fn delete(&mut self, entry: Uuid) -> Result<bool> {
Ok(FileWriter::delete(&self.path, entry)?)
}
fn clear(&mut self) -> Result<()> {
for entry in self.iter()?.filter_map(Result::ok) {
self.delete(entry)?;
}
Ok(())
}
fn iter(&self) -> Result<Self::Iterator> {
Iter::open(&self.path)
}
}
#[derive(Debug)]
pub struct FileReader {
file: BufReader<fs::File>
}
impl FileReader {
fn read<P>(path: P, entry: Uuid) -> io::Result<Self>
where P: AsRef<Path> {
Self::open(fs::OpenOptions::new().read(true), path, entry)
}
fn open<P>(options: &fs::OpenOptions, path: P, entry: Uuid) -> io::Result<Self>
where P: AsRef<Path> {
let path = path.as_ref().join(entry.to_string());
match options.open(&path) {
Ok(file) => Ok(Self { file: BufReader::new(file) }),
Err(e) => Err(e)
}
}
}
impl io::Read for FileReader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.file.read(buf)
}
}
#[derive(Debug)]
pub struct FileWriter {
file: BufWriter<fs::File>
}
impl FileWriter {
fn new<P>(path: P, entry: Uuid) -> io::Result<Self>
where P: AsRef<Path> {
Self::open(fs::OpenOptions::new().create(true).write(true), path, entry)
}
fn write<P>(path: P, entry: Uuid) -> io::Result<Self>
where P: AsRef<Path> {
Self::open(fs::OpenOptions::new().create(true).write(true).truncate(true), path, entry)
}
fn overwrite<P>(path: P, entry: Uuid) -> io::Result<Self>
where P: AsRef<Path> {
Self::open(fs::OpenOptions::new().write(true).truncate(true), path, entry)
}
fn delete<P>(path: P, entry: Uuid) -> io::Result<bool>
where P: AsRef<Path> {
match fs::remove_file(path.as_ref().join(entry.to_string())) {
Ok(_) => Ok(true),
Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(false),
Err(e) => Err(e)
}
}
fn open<P>(options: &fs::OpenOptions, path: P, entry: Uuid) -> io::Result<Self>
where P: AsRef<Path> {
let path = path.as_ref().join(entry.to_string());
match options.open(&path) {
Ok(file) => Ok(Self { file: BufWriter::new(file) }),
Err(e) => Err(e)
}
}
}
impl io::Write for FileWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.file.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.file.flush()
}
}
#[derive(Debug)]
pub struct Iter {
iterator: fs::ReadDir
}
impl Iter {
fn open<P>(path: P) -> Result<Self>
where P: AsRef<Path> {
Ok(Self {
iterator: path.as_ref().read_dir()?
})
}
}
impl Iterator for Iter {
type Item = Result<Uuid>;
fn next(&mut self) -> Option<Self::Item> {
let into_id = |result: io::Result<fs::DirEntry>| -> Result<Uuid> {
result
.map_err(From::from)
.and_then(|entry| {
entry.file_name()
.into_string()
.map_err(|e| StorageError::invalid_uuid(format!("found file in directory that has invalid unicode character in his name: {:?}", e)))
})
.and_then(|name| {
Uuid::parse_str(&name).map_err(From::from)
})
};
self.iterator
.next()
.map(into_id)
}
}
#[derive(Debug, Clone)]
#[cfg(feature = "directory_locks")]
pub struct LockedDirectory(Directory);
#[cfg(feature = "directory_locks")]
impl LockedDirectory {
pub fn new<P>(path: P) -> Result<Self>
where P: AsRef<Path> {
Ok(Self(Directory::new(path)?))
}
}
#[cfg(feature = "directory_locks")]
impl Storage for LockedDirectory {
type Read = LockedFileReader;
type Write = LockedFileWriter;
type Iterator = Iter;
fn new(&mut self) -> Result<(Uuid, Self::Write)> {
let (id, writer) = self.0.new()?;
Ok((id, LockedFileWriter::wrap(writer)?))
}
fn read(&self, entry: Uuid) -> Result<Self::Read> {
Ok(LockedFileReader::wrap(self.0.read(entry)?)?)
}
fn write(&mut self, entry: Uuid) -> Result<Self::Write> {
Ok(LockedFileWriter::wrap(self.0.write(entry)?)?)
}
fn overwrite(&mut self, entry: Uuid) -> Result<Self::Write> {
Ok(LockedFileWriter::wrap(self.0.overwrite(entry)?)?)
}
fn delete(&mut self, entry: Uuid) -> Result<bool> {
self.0.delete(entry)
}
fn clear(&mut self) -> Result<()> {
self.0.clear()
}
fn iter(&self) -> Result<Self::Iterator> {
self.0.iter()
}
}
#[derive(Debug)]
#[cfg(feature = "directory_locks")]
pub struct LockedFileReader(FileReader);
#[cfg(feature = "directory_locks")]
impl LockedFileReader {
fn wrap(mut reader: FileReader) -> Result<Self> {
reader.file.get_mut().lock_shared()?;
Ok(Self(reader))
}
}
#[cfg(feature = "directory_locks")]
impl Drop for LockedFileReader {
fn drop(&mut self) {
self.0.file.get_mut().unlock().expect("unable to unlock file")
}
}
#[cfg(feature = "directory_locks")]
impl io::Read for LockedFileReader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
}
#[derive(Debug)]
#[cfg(feature = "directory_locks")]
pub struct LockedFileWriter(FileWriter);
#[cfg(feature = "directory_locks")]
impl LockedFileWriter {
fn wrap(mut reader: FileWriter) -> Result<Self> {
reader.file.get_mut().lock_exclusive()?;
Ok(Self(reader))
}
}
#[cfg(feature = "directory_locks")]
impl Drop for LockedFileWriter {
fn drop(&mut self) {
self.0.file.get_mut().unlock().expect("unable to unlock file")
}
}
#[cfg(feature = "directory_locks")]
impl io::Write for LockedFileWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
}
}