#![allow(clippy::unnecessary_debug_formatting)]
use std::{
io::{Error, ErrorKind, SeekFrom},
mem::{self, ManuallyDrop},
path::{Path, PathBuf},
sync::{Arc, Mutex, MutexGuard},
};
use crate::{
dir_entry::DirEntry,
file::{File, FileInner},
metadata::Metadata,
open_options::OpenOptions,
walkdir::{WalkDir, WalkdirIterator, WalkdirIteratorInner},
Diskit,
};
pub type LogFunc = Box<dyn Fn(String) + Send>;
#[allow(missing_docs)]
#[derive(Clone)]
pub struct LogDiskit<D>
where
D: Diskit,
{
pub inner: D,
pub log: Arc<Mutex<LogFunc>>,
}
impl<D> LogDiskit<D>
where
D: Diskit,
{
pub fn new(inner: D, log: LogFunc) -> Self
{
Self::new_raw(inner, Arc::new(Mutex::new(log)))
}
pub fn new_raw(inner: D, log: Arc<Mutex<LogFunc>>) -> Self
{
Self { inner, log }
}
pub fn set_log_func(&mut self, log: LogFunc)
{
self.set_log_func_raw(Arc::new(Mutex::new(log)));
}
pub fn set_log_func_raw(&mut self, log: Arc<Mutex<LogFunc>>)
{
self.log = log;
}
fn lock_log(&self) -> Result<MutexGuard<'_, LogFunc>, ErrorKind>
{
self.log.lock().map_err(|_| ErrorKind::Other)
}
}
impl<D> Default for LogDiskit<D>
where
D: Diskit,
{
fn default() -> Self
{
Self::new(D::default(), get_standard_log_func())
}
}
impl<D> Diskit for LogDiskit<D>
where
D: Diskit,
{
fn set_pwd_inner(&self, path: &Path) -> Result<(), Error>
{
let log = self.lock_log()?;
log(format!("Setting pwd to {path:?}."));
match self.inner.set_pwd_inner(path)
{
Ok(()) =>
{
log(format!("Set pwd to {path:?}."));
Ok(())
}
Err(err) =>
{
log(format!("Failed to set pwd to {path:?}: {err:?}."));
Err(err)
}
}
}
fn get_pwd(&self) -> Result<PathBuf, Error>
{
let log = self.lock_log()?;
log("Getting pwd.".to_owned());
match self.inner.get_pwd()
{
Ok(path) =>
{
log(format!("Got pwd: {path:?}."));
Ok(path)
}
Err(err) =>
{
log(format!("Failed to get pwd: {err:?}."));
Err(err)
}
}
}
fn open_inner(&self, path: &Path) -> Result<File<Self>, Error>
{
let log = self.lock_log()?;
log(format!("Opening {path:?}."));
match self.inner.open_inner(path)
{
Ok(file) =>
{
let inner = mem::replace(
&mut ManuallyDrop::new(file).inner,
FileInner { file: None, val: 0 },
);
log(format!("Opened {path:?}: {inner:?}."));
Ok(File {
inner,
diskit: self.clone(),
})
}
Err(err) =>
{
log(format!("Failed to open {path:?}: {err:?}."));
Err(err)
}
}
}
fn create_inner(&self, path: &Path) -> Result<File<Self>, Error>
{
let log = self.lock_log()?;
log(format!("Creating {path:?}."));
match self.inner.create_inner(path)
{
Ok(file) =>
{
let inner = mem::replace(
&mut ManuallyDrop::new(file).inner,
FileInner { file: None, val: 0 },
);
log(format!("Created {path:?}: {inner:?}."));
Ok(File {
inner,
diskit: self.clone(),
})
}
Err(err) =>
{
log(format!("Failed to create {path:?}: {err:?}."));
Err(err)
}
}
}
fn open_with_options_inner(
&self,
path: &Path,
options: OpenOptions,
) -> Result<File<Self>, Error>
{
let log = self.lock_log()?;
log(format!("Opening {path:?} with options {options:?}."));
match self.inner.open_with_options_inner(path, options)
{
Ok(file) =>
{
let inner = mem::replace(
&mut ManuallyDrop::new(file).inner,
FileInner { file: None, val: 0 },
);
log(format!(
"Opened {path:?} with options {options:?}: {inner:?}."
));
Ok(File {
inner,
diskit: self.clone(),
})
}
Err(err) =>
{
log(format!(
"Failed to open {path:?} with options {options:?}: {err:?}."
));
Err(err)
}
}
}
fn read_inner(&self, file: &FileInner, buf: &mut [u8]) -> Result<usize, Error>
{
let log = self.lock_log()?;
log(format!("Reading {file:?} into {buf:?}."));
match self.inner.read_inner(file, buf)
{
Ok(n) =>
{
log(format!("Read {n:?} bytes of {file:?}: {buf:?}."));
Ok(n)
}
Err(err) =>
{
log(format!("Failed to read {file:?} into {buf:?}: {err:?}."));
Err(err)
}
}
}
fn read_to_end_inner(&self, file: &mut FileInner, buf: &mut Vec<u8>) -> Result<usize, Error>
{
let log = self.lock_log()?;
log(format!("Reading {file:?} to end into {buf:?}."));
match self.inner.read_to_end_inner(file, buf)
{
Ok(n) =>
{
log(format!("Read {n:?} bytes of {file:?} to end: {buf:?}."));
Ok(n)
}
Err(err) =>
{
log(format!(
"Failed to read {file:?} into {buf:?} to end: {err:?}."
));
Err(err)
}
}
}
fn read_to_string_inner(&self, file: &mut FileInner, buf: &mut String) -> Result<usize, Error>
{
let log = self.lock_log()?;
log(format!("Reading {file:?} to string into {buf:?}."));
match self.inner.read_to_string_inner(file, buf)
{
Ok(n) =>
{
log(format!("Read {n:?} bytes of {file:?} to string: {buf:?}."));
Ok(n)
}
Err(err) =>
{
log(format!(
"Failed to read {file:?} into {buf:?} to string: {err:?}."
));
Err(err)
}
}
}
fn write_inner(&self, file: &mut FileInner, buf: &[u8]) -> Result<usize, Error>
{
let log = self.lock_log()?;
log(format!("Writing {buf:?} into {file:?}."));
match self.inner.write_inner(file, buf)
{
Ok(n) =>
{
log(format!("Wrote {n:?} bytes of {buf:?} in {file:?}."));
Ok(n)
}
Err(err) =>
{
log(format!("Failed to write {buf:?} into {file:?}: {err:?}."));
Err(err)
}
}
}
fn write_all_inner(&self, file: &mut FileInner, buf: &[u8]) -> Result<(), Error>
{
let log = self.lock_log()?;
log(format!("Writing all of {buf:?} into {file:?}."));
match self.inner.write_all_inner(file, buf)
{
Ok(()) =>
{
log(format!("Wrote all of {buf:?} in {file:?}."));
Ok(())
}
Err(err) =>
{
log(format!(
"Failed to write all of {buf:?} into {file:?}: {err:?}."
));
Err(err)
}
}
}
fn flush_inner(&self, file: &mut FileInner) -> Result<(), Error>
{
let log = self.lock_log()?;
log(format!("Flushing {file:?}."));
match self.inner.flush_inner(file)
{
Ok(()) =>
{
log(format!("Flushed {file:?}."));
Ok(())
}
Err(err) =>
{
log(format!("Failed to flush {file:?}: {err:?}."));
Err(err)
}
}
}
fn metadata_inner(&self, file: &FileInner) -> Result<Metadata, Error>
{
let log = self.lock_log()?;
log(format!("Retrieving metadata of {file:?}."));
match self.inner.metadata_inner(file)
{
Ok(metadata) =>
{
log(format!("Retrieved metadata of {file:?}: {metadata:?}."));
Ok(metadata)
}
Err(err) =>
{
log(format!("Failed to retrieve metadata of {file:?}: {err:?}."));
Err(err)
}
}
}
fn seek_inner(&self, file: &mut FileInner, pos: SeekFrom) -> Result<u64, Error>
{
let log = self.lock_log()?;
log(format!("Seeking {file:?} to {pos:?}."));
match self.inner.seek_inner(file, pos)
{
Ok(pos_rv) =>
{
log(format!(
"Seeked {file:?} to {pos_rv:?} (requested: {pos:?})."
));
Ok(pos_rv)
}
Err(err) =>
{
log(format!("Failed to seek {file:?} to {pos:?}: {err:?}."));
Err(err)
}
}
}
fn create_dir_inner(&self, path: &Path) -> Result<(), Error>
{
let log = self.lock_log()?;
log(format!("Creating directory {path:?}."));
match self.inner.create_dir_inner(path)
{
Ok(()) =>
{
log(format!("Created directory {path:?}."));
Ok(())
}
Err(err) =>
{
log(format!("Failed to create directory {path:?}: {err:?}."));
Err(err)
}
}
}
fn create_dir_all_inner(&self, path: &Path) -> Result<(), Error>
{
let log = self.lock_log()?;
log(format!("Creating directory {path:?} recursively."));
match self.inner.create_dir_all_inner(path)
{
Ok(()) =>
{
log(format!("Created directory {path:?} recursively."));
Ok(())
}
Err(err) =>
{
log(format!(
"Failed to create directory {path:?} recursively: {err:?}."
));
Err(err)
}
}
}
fn close_inner(&self, file: FileInner) -> Result<(), Error>
{
let log = self.lock_log()?;
log(format!("Closing {file:?}."));
match self.inner.close_inner(file)
{
Ok(()) =>
{
log("Closed a file.".to_string());
Ok(())
}
Err(err) =>
{
log(format!("Failed to close a file: {err:?}."));
Err(err)
}
}
}
fn walkdir_inner(&self, path: &Path) -> WalkDir<Self>
{
let log = self
.lock_log()
.expect("Failed to get lock on logging function");
log(format!("Constructing `WalkDir` for {path:?}."));
let WalkDir { diskit: _, options } = self.inner.walkdir_inner(path);
log(format!("Constructed `WalkDir` for {path:?}: {options:?}."));
WalkDir {
diskit: self.clone(),
options,
}
}
fn into_walkdir_iterator(&self, walkdir: WalkDir<Self>) -> WalkdirIterator<Self>
{
let log = self
.lock_log()
.expect("Failed to get lock on logging function");
log(format!(
"Constructing `WalkDirIterator` from {:?}.",
walkdir.options
));
let WalkdirIterator { inner } = self.inner.into_walkdir_iterator(WalkDir {
diskit: self.inner.clone(),
options: walkdir.options.clone(),
});
match inner
{
Ok((inner, _)) =>
{
log(format!(
"Constructed `WalkDirIterator` from {:?}: {inner:?}.",
walkdir.options,
));
WalkdirIterator {
inner: Ok((inner, self.clone())),
}
}
Err(err) =>
{
log(format!(
"Failed to construct `WalkDirIterator` from {:?}: {err:?}.",
walkdir.options
));
WalkdirIterator { inner: Err(err) }
}
}
}
fn walkdir_next_inner(
&self,
inner: &mut WalkdirIteratorInner,
) -> Option<Result<DirEntry, Error>>
{
let log = self.lock_log().ok()?;
log(format!("Getting next file from {inner:?}."));
match self.inner.walkdir_next_inner(inner)
{
Some(Ok(dir_entry)) =>
{
log(format!("Got next file from {inner:?}: {dir_entry:?}."));
Some(Ok(dir_entry))
}
Some(Err(err)) =>
{
log(format!("Failed to get next file from {inner:?}: {err:?}."));
Some(Err(err))
}
None =>
{
log(format!("Iterator {inner:?} is finished."));
None
}
}
}
#[cfg(feature = "trash")]
fn trash_delete_inner(&self, path: &Path) -> Result<(), trash::Error>
{
let log = self.lock_log().map_err(|_| {
trash::into_unknown(format!("Failed to acquire lock to trash {path:?}"))
})?;
log(format!("Trashing {path:?}."));
match self.inner.trash_delete_inner(path)
{
Ok(()) =>
{
log(format!("Thrashed {path:?}."));
Ok(())
}
Err(err) =>
{
log(format!("Failed to trash {path:?}: {err:?}."));
Err(err)
}
}
}
}
#[must_use]
pub fn get_standard_log_func() -> LogFunc
{
Box::new(|msg| println!("{msg}"))
}
#[allow(clippy::missing_panics_doc)]
#[must_use]
pub fn get_log_in_buf_func(buf: Arc<Mutex<String>>) -> LogFunc
{
Box::new(move |s| {
let mut lock = buf.lock().unwrap();
lock.push_str(&s);
lock.push('\n');
})
}