caco 0.5.33

library for dealing with doom [et al] wad files
Documentation
use std::ops::{Deref, DerefMut};
use crate::agnostic::{Clump, ClumpMut};
use anyhow::Result;

/// A handle to a clump as it is interpreted with a specified format.
/// 
/// This struct dereferences to the format in question. A [`Formatted`]
/// using the format [`Playpal`](crate::fmts::d2::palette::Playpal) will
/// function as a [`Playpal`](crate::fmts::d2::palette::Playpal) in code.
/// The formatted data can be read from or mutated.
/// 
/// Using the [`commit`](Formatted::commit) method after modifying the
/// formatted data will reserialise it into its raw binary form and
/// write it back to underlying clump. This allows you to modify a clump
/// in place while interpreting it as a specific format. This is achieved
/// with [`try_interpret_as`](FormattableClumpMut::try_interpret_as).
pub struct Formatted<'a, R: ClumpMut, F: ClumpFormat> {
    r: &'a mut R,
    f: F,
}

impl<'a, R, F> Deref for Formatted<'a, R, F> where R: ClumpMut, F: ClumpFormat {
    type Target = F;

    fn deref(&self) -> &F {
        &self.f
    }
}

impl<'a, R, F> DerefMut for Formatted<'a, R, F> where R: ClumpMut, F: ClumpFormat {
    fn deref_mut(&mut self) -> &mut F {
        &mut self.f
    }
}

pub trait ClumpFormat where Self: Sized {
    /// Try to decode raw bytes into this format. Returns [`Ok`]
    /// or [`Err`] based on if the binary data was valid for the
    /// format.
    fn try_decode_bytes(data: &[u8]) -> Result<Self>;

    /// Try to encode this formatted data into raw binary data.
    /// This may fail if the formatted data in memory is able to
    /// end up in an invalid state, in which case it will refuse
    /// to serialise.
    fn try_encode_bytes(&self) -> Result<Box<[u8]>>;
}

pub trait FormattableClump where Self: Clump {
    /// Try to get formatted data out of the clump.
    fn try_view_as<F>(&self) -> Result<F> where F: ClumpFormat {
        F::try_decode_bytes(self.bytestuff())
    }
}

impl<H: Clump> FormattableClump for H {}

pub trait FormattableClumpMut where Self: ClumpMut + Sized {
    /// Try to insert formatted data into the clump by converting
    /// it to raw binary.
    fn try_replace_with<F>(&mut self, f: &F) -> Result<()> where F: ClumpFormat {
        match f.try_encode_bytes() {
            Ok(b) => Ok(self.replace(&b[..])),
            Err(e) => Err(e)
        }
    }

    /// Attempts to create a [`Formatted`] from the clump for the
    /// specified format. See [`Formatted`]'s documentation for
    /// more information.
    fn try_interpret_as<'a, F>(&'a mut self) -> Result<Formatted<'a, Self, F>> where F: ClumpFormat {
        match F::try_decode_bytes(self.bytestuff()) {
            Ok(f) => Ok(Formatted { r: self, f }),
            Err(e) => Err(e),
        }
    }
}

impl<H: ClumpMut + Sized> FormattableClumpMut for H {}

impl<'a, R, F> Formatted<'a, R, F> where R: ClumpMut, F: ClumpFormat {
    /// Reloads the formatted data from the underlying clump.
    /// 
    /// If you've modified the formatted data, this will undo your changes back
    /// to the state the content was in the last time data was saved to the
    /// clump, or if it hasn't been saved, then back to the original state
    /// it was loaded in.
    pub fn rollback(&mut self) {
        // shouldn't panic, since r is only modified when data is valid, 
        // and the struct is only created if the data is valid.
        // -nf
        self.f = F::try_decode_bytes(self.r.bytestuff()).unwrap();
    }

    /// Attempts to save changes to the underlying clump.
    /// 
    /// If the data is valid, this returns [`Ok`] and the clump is modified.
    /// 
    /// If the data is invalid, this will return an [`Err`] value, and the clump
    /// is not modified from its original state.
    pub fn commit(&mut self) -> Result<()> {
        match self.f.try_encode_bytes() {
            Ok(b) => {
                self.r.replace(&b[..]);
                Ok(())
            },

            Err(e) => Err(e)
        }
    }
}