diskit 0.1.1

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

use std::fs;

/// Transparent replica of [`OpenOptions`](std::fs::OpenOptions)
///
/// This is a [replica](crate#making-your-own-diskit) of
/// [`OpenOptions`](std::fs::OpenOptions).  Please see it's
/// documentation for what which option means and how they interact.
///
/// There are [`From`] implementations to cast between this type and
/// [`fs::OpenOptions`] back and forth, but you shouldn't rely on the
/// cast from [`fs::OpenOptions`] to this type to be stable and always
/// successful.
// This pedantic lint is supposed to guard against implementing state
// machines as structs with a `bool` for every state instead of using
// enums.  This is not a state machine, so it's actually completely
// fine.
#[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Clone, Copy)]
// See lib.rs for justification.
#[allow(missing_docs)]
pub struct OpenOptions
{
    pub read: bool,
    pub write: bool,
    pub append: bool,
    pub truncate: bool,
    pub create: bool,
    pub create_new: bool,
}

impl Default for OpenOptions
{
    fn default() -> Self
    {
        Self::new()
    }
}

// This pedantic lint guards against implementing constructors and
// then throwing away the result.  This is wrong here since apart from
// `OpenOptions::new` (which already has an `#[must_use]` attribute)
// these aren't constructors and the `Self` they return is just a nice
// help for writing method-style function chains (or however that
// concept is called.  See the `From` implementation below to see what
// I mean).
#[allow(clippy::return_self_not_must_use)]
impl OpenOptions
{
    /// Creates a new `OpenOptions`
    ///
    /// Creates a new all-false `OpenOptions`.
    #[must_use]
    pub const fn new() -> Self
    {
        Self {
            read: false,
            write: false,
            append: false,
            truncate: false,
            create: false,
            create_new: false,
        }
    }

    /// Sets the `read` flag
    ///
    /// This function sets or resets the `read` flag and returns a
    /// copy of it to enable method-style function chains.
    pub fn read(&mut self, flag: bool) -> Self
    {
        self.read = flag;
        *self
    }

    /// Sets the `write` flag
    ///
    /// This function sets or resets the `write` flag and returns a
    /// copy of it to enable method-style function chains.
    pub fn write(&mut self, flag: bool) -> Self
    {
        self.write = flag;
        *self
    }

    /// Sets the `append` flag
    ///
    /// This function sets or resets the `append` flag and returns a
    /// copy of it to enable method-style function chains.
    pub fn append(&mut self, flag: bool) -> Self
    {
        self.append = flag;
        *self
    }

    /// Sets the `truncate` flag
    ///
    /// This function sets or resets the `truncate` flag and returns a
    /// copy of it to enable method-style function chains.
    pub fn truncate(&mut self, flag: bool) -> Self
    {
        self.truncate = flag;
        *self
    }

    /// Sets the `create` flag
    ///
    /// This function sets or resets the `create` flag and returns a
    /// copy of it to enable method-style function chains.
    pub fn create(&mut self, flag: bool) -> Self
    {
        self.create = flag;
        *self
    }

    /// Sets the `create_new` flag
    ///
    /// This function sets or resets the `create_new` flag and returns
    /// a copy of it to enable method-style function chains.
    pub fn create_new(&mut self, flag: bool) -> Self
    {
        self.create_new = flag;
        *self
    }
}

impl From<&fs::OpenOptions> for OpenOptions
{
    fn from(open_options: &fs::OpenOptions) -> Self
    {
        let mut rv = Self::new();

        for line in format!("{:#?}", open_options).lines()
        {
            let line: &str = line;

            if line.starts_with("        read: ")
            {
                rv.read = line.as_bytes()[14] == b't';
            }

            if line.starts_with("        write: ")
            {
                rv.write = line.as_bytes()[15] == b't';
            }

            if line.starts_with("        append: ")
            {
                rv.append = line.as_bytes()[16] == b't';
            }

            if line.starts_with("        truncate: ")
            {
                rv.truncate = line.as_bytes()[18] == b't';
            }

            if line.starts_with("        create: ")
            {
                rv.create = line.as_bytes()[16] == b't';
            }

            if line.starts_with("        create_new: ")
            {
                rv.create_new = line.as_bytes()[20] == b't';
            }
        }

        rv
    }
}

impl From<OpenOptions> for fs::OpenOptions
{
    fn from(open_options: OpenOptions) -> Self
    {
        let mut rv = Self::new();

        rv.read(open_options.read);
        rv.write(open_options.write);
        rv.append(open_options.append);
        rv.truncate(open_options.truncate);
        rv.create(open_options.create);
        rv.create_new(open_options.create_new);

        rv
    }
}

#[cfg(test)]
mod tests
{
    use std::fs;

    use super::OpenOptions;

    #[test]
    fn deconstruct_test()
    {
        for i in 0..(1 << 6)
        {
            let options: OpenOptions = From::from(
                &*fs::OpenOptions::new()
                    .read(i % 2 == 1)
                    .write((i / 2) % 2 == 1)
                    .append((i / 4) % 2 == 1)
                    .truncate((i / 8) % 2 == 1)
                    .create((i / 16) % 2 == 1)
                    .create_new((i / 32) % 2 == 1),
            );

            let num: u8 = options.read as u8
                + options.write as u8 * 2
                + options.append as u8 * 4
                + options.truncate as u8 * 8
                + options.create as u8 * 16
                + options.create_new as u8 * 32;

            assert_eq!(num, i);
        }
    }
}