diskit 0.1.1

Utilities for intercepting disk requests.
Documentation
//! Transparent replica of [`File`](std::fs::File)
//!
//! For more information see the [struct level documentation](File).

use std::{
    fs,
    io::{Error, Read, Seek, SeekFrom, Write},
    mem::{self, ManuallyDrop},
};

use crate::{metadata::Metadata, Diskit};

/// Implementation helper for [`File`]
///
/// This is the struct which the methods of the [`Diskit`](crate::Diskit)
/// trait receive when they need a file.
///
/// [`file`](Self::file) wraps an [`fs::File`] so that
/// [`StdDiskit`](crate::StdDiskit) is more efficient, so it shouldn't
/// be used by any other diskit.
///
/// In [`val`](Self::val) you can store one [`usize`] worth of data.
/// If this is not sufficient you have to store the excess data in
/// your type that implements [`Diskit`](crate::Diskit) and make this
/// an index in your struct.
///
/// While all this struct's fields are (necessarily) `pub` they should
/// not be changed by anyone or anything apart from the original
/// diskit that created it.
// See lib.rs for justification.
#[allow(missing_docs)]
// False positive of pedantic lint.  I think this is the best name.
#[allow(clippy::module_name_repetitions)]
#[derive(Debug)]
pub struct FileInner
{
    pub file: Option<fs::File>,
    pub val: usize,
}

/// Transparent replica of [`File`](std::fs::File)
///
/// This is a [replica](crate#making-your-own-diskit) of
/// [`File`](std::fs::File).  It should work just as the original, so
/// please look the for documentation.
// See lib.rs for justification.
#[allow(missing_docs)]
#[derive(Debug)]
pub struct File<D>
where
    D: Diskit,
{
    pub inner: FileInner,
    pub diskit: D,
}

impl<D> Read for File<D>
where
    D: Diskit,
{
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error>
    {
        self.diskit.read_inner(&self.inner, buf)
    }

    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize, Error>
    {
        self.diskit.read_to_end_inner(&mut self.inner, buf)
    }

    fn read_to_string(&mut self, buf: &mut String) -> Result<usize, Error>
    {
        self.diskit.read_to_string_inner(&mut self.inner, buf)
    }
}

impl<D> Write for File<D>
where
    D: Diskit,
{
    fn write(&mut self, buf: &[u8]) -> Result<usize, Error>
    {
        self.diskit.write_inner(&mut self.inner, buf)
    }

    fn write_all(&mut self, buf: &[u8]) -> Result<(), Error>
    {
        self.diskit.write_all_inner(&mut self.inner, buf)
    }

    fn flush(&mut self) -> std::io::Result<()>
    {
        self.diskit.flush_inner(&mut self.inner)
    }
}

impl<D> Seek for File<D>
where
    D: Diskit,
{
    fn seek(&mut self, pos: SeekFrom) -> Result<u64, Error>
    {
        self.diskit.seek_inner(&mut self.inner, pos)
    }
}

impl<D> Drop for File<D>
where
    D: Diskit,
{
    fn drop(&mut self)
    {
        let diskit = self.diskit.clone();
        let file = mem::replace(
            &mut ManuallyDrop::new(self).inner,
            FileInner { file: None, val: 0 },
        );

        let file_copy = (file.file.is_some(), file.val);

        if let Err(err) = diskit.close_inner(file)
        {
            eprintln!("Error in closing file {file_copy:?}: {err:?}");
        }
    }
}

impl<D> File<D>
where
    D: Diskit,
{
    /// Retrieves the metadata of the file
    ///
    /// Please see [`crate::Diskit::metadata_inner`] for more
    /// information.
    pub fn metadata(&self) -> Result<Metadata, Error>
    {
        self.diskit.metadata_inner(&self.inner)
    }

    /// Closes the file
    ///
    /// Please see [`crate::Diskit::close_inner`] for more
    /// information.
    pub fn close(self) -> Result<(), Error>
    {
        let diskit = self.diskit.clone();
        diskit.close_inner(mem::replace(
            &mut ManuallyDrop::new(self).inner,
            FileInner { file: None, val: 0 },
        ))
    }
}