avr-oxide 0.3.0

An extremely simple Rusty operating system for AVR microcontrollers
/* persist.rs
 *
 * Developed by Tim Walls <tim.walls@snowgoons.com>
 * Copyright (c) All Rights Reserved, Tim Walls
 */
//! An incredibly simple implementation for persisting data types to/from
//! a writer/reader.
//!
//! Why use this instead of something nice like serde and postcard?  Because
//! most of the time, those things depend on stuff we don't have in the AVR
//! world (not just no_std, but missing things like atomic datatypes),
//! and even when you do get them to build - they're just too big for our
//! devices.
//!
//! # Usage
//! Simply implement the `Persist` trait on anything you wish to be able
//! to load or save from anything which implements avr_oxide::io::Read or
//! avr_oxide::io::Write.
//!
//! Implementations are provided for the basic numeric types and for Vec
//! types.
//!
//! Furthermore, in the `oxide_macros::Persist` there is a Derive trait
//! provided that will implement for simple Structs or Enums:
//!
//! ```rust,no_run
//! use oxide_macros::Persist;
//! use avr_oxide::util::persist::Persist;
//! use avr_oxide::hal::generic::eeprom::EepromSpace;
//! use avr_oxide::panic_if_err;
//!
//! #[derive(Persist)]
//! enum MyEnumeration {
//!   Something,
//!   SomethingNumbered(u8),
//!   SomethingComplex { first: u16, second: u32 }
//! }
//!
//! #[derive(Persist)]
//! #[persist(magicnumber = 1)]
//! struct MyDataStructure {
//!   number: u8,
//!   vector: Vec<u16>,
//!   something: MyEnumeration
//! }
//!
//! pub fn load_save_object() -> ! {
//!   let eeprom = &mut avr_oxide::hardware::nvm::eeprom::instance();
//!
//!   let mut thing : MyDataStructure = panic_if_err!(Persist::load_with(eeprom.reader()));
//!   thing.number += 1u8;
//!   panic_if_err!(thing.save_with(eeprom.writer()));
//!
//!   loop{}
//! }
//! ```
//!
//! The optional attribute `magicnumber` allows you to specify the 'magic
//! number' (`u8`) which will be used to identify this structure.  This can
//! be used for versioning your structure, to ensure that deserialisation will
//! fail if the magic number (or version) does not match.



// Imports ===================================================================
use avr_oxide::io::{IoError, Read, Write};
use avr_oxide::alloc::vec::Vec;

pub mod derive {
  pub use oxide_macros::Persist;
}

// Declarations ==============================================================
pub enum PersistenceError {
  SerialisationFailed,

  DeserialisationFailed,

  IoError
}

pub type PersistenceResult<T> = Result<T,PersistenceError>;

/**
 * Trait implemented by types that can be read/written from a Reader/Writer
 * as a simple binary serialisation format.
 */
pub trait Persist : Sized {
  /// Load an instance of this type from the given reader.
  fn load_from<R: Read>(reader: &mut R) -> PersistenceResult<Self>;

  /// Load an instance of this type from the given reader, or return the Default
  /// instantiation if it cannot be loaded.
  fn load_from_or_default<R: Read>(reader: &mut R) -> Self where Self: Default {
    match Self::load_from(reader) {
      Ok(me) => me,
      Err(_e) => Self::default()
    }
  }

  /// Save an instance of this type to the given writer.
  fn save_to<W: Write>(&self, writer: &mut W) -> PersistenceResult<()>;

  /// Take the given reader and use it to load an instance of this type.
  fn load_with<R: Read>(mut reader: R) -> PersistenceResult<Self> {
    Self::load_from(&mut reader)
  }

  /// Take the given reader and use it to load an instance of this type,
  /// or return the Default instantiation if it cannot be loaded.
  fn load_with_or_default<R: Read>(mut reader: R) -> Self where Self: Default {
    Self::load_from_or_default(&mut reader)
  }

  /// Take the given writer and use it to save an instance of this type.
  fn save_with<W: Write>(&self, mut writer: W) -> PersistenceResult<()> {
    self.save_to(&mut writer)
  }
}


// Code ======================================================================
impl From<IoError> for PersistenceError {
  fn from(_: IoError) -> Self {
    PersistenceError::IoError
  }
}

impl Persist for u8 {
  fn load_from<R: Read>(reader: &mut R) -> PersistenceResult<Self> {
    let mut buf : [u8; 1] = [ 0 ];

    reader.read(&mut buf)?;

    Ok(buf[0])
  }

  fn save_to<W: Write>(&self, writer: &mut W) -> PersistenceResult<()> {
    let buf: [u8; 1] = [*self];

    match writer.write(&buf) {
      Ok(1) => {
        Ok(())
      },
      Ok(_) => {
        Err(PersistenceError::IoError)
      },
      Err(e) => Err(e.into())
    }
  }
}

impl<T, const WIDTH: usize> Persist for [T; WIDTH]
where
  T: Persist
{

  fn load_from<R: Read>(reader: &mut R) -> PersistenceResult<Self> {
    let mut array : [core::mem::MaybeUninit<T>; WIDTH] = core::mem::MaybeUninit::uninit_array();

    for element in &mut array {
      element.write(T::load_from(reader)?);
    }

    Ok(unsafe { core::mem::MaybeUninit::array_assume_init(array) })
  }

  fn save_to<W: Write>(&self, writer: &mut W) -> PersistenceResult<()> {
    for item in self {
      item.save_to(writer)?;
    }
    Ok(())
  }
}

impl Persist for u16 {
  fn load_from<R: Read>(reader: &mut R) -> PersistenceResult<Self> {
    let buf : [u8;2] = Persist::load_from(reader)?;

    Ok((buf[0] as u16) << 8 | (buf[1] as u16))
  }

  fn save_to<W: Write>(&self, writer: &mut W) -> PersistenceResult<()> {
    let buf  = [
      ((self >> 8) & 0xff) as u8,
      ((self >> 0) & 0xff) as u8 ];

    buf.save_to(writer)
  }
}

impl Persist for usize {
  fn load_from<R: Read>(reader: &mut R) -> PersistenceResult<Self> {
    let val:u16 = Persist::load_from(reader)?;

    Ok(val as usize)
  }

  fn save_to<W: Write>(&self, writer: &mut W) -> PersistenceResult<()> {
    (*self as u16).save_to(writer)
  }
}


impl Persist for u32 {
  fn load_from<R: Read>(reader: &mut R) -> PersistenceResult<Self> {
    let buf : [u8;4] = Persist::load_from(reader)?;

    Ok((buf[0] as u32) << 24 | (buf[1] as u32) << 16 | (buf[2] as u32) << 8 | (buf[3] as u32))
  }

  fn save_to<W: Write>(&self, writer: &mut W) -> PersistenceResult<()> {
    let buf  = [
      ((self >> 24) & 0xff) as u8,
      ((self >> 16) & 0xff) as u8,
      ((self >> 8)  & 0xff) as u8,
      ((self >> 0)  & 0xff) as u8 ];

    buf.save_to(writer)
  }
}


impl<T> Persist for Vec<T>
where
  T: Persist
{

  fn load_from<R: Read>(reader: &mut R) -> PersistenceResult<Self> {
    let tag:u8 = Persist::load_from(reader)?;
    if tag != b'V' {
      return Err(PersistenceError::DeserialisationFailed);
    }

    let count:usize = Persist::load_from(reader)?;

    let mut vector : Vec<T> = Vec::with_capacity(count);

    for _i in 0..count {
      vector.push(Persist::load_from(reader)?)
    }

    Ok(vector)
  }

  fn save_to<W: Write>(&self, writer: &mut W) -> PersistenceResult<()> {
    b'V'.save_to(writer)?;
    self.len().save_to(writer)?;
    for element in self {
      (*element).save_to(writer)?;
    }
    Ok(())
  }
}

impl<T> Persist for Option<T>
where
  T: Persist
{
  fn load_from<R: Read>(reader: &mut R) -> PersistenceResult<Self> {
    let tag:u8 = Persist::load_from(reader)?;

    match tag {
      b'N' => Ok(Self::None),
      b'S' => Ok(Some(T::load_from(reader)?)),
      _    => Err(PersistenceError::DeserialisationFailed)
    }
  }

  fn save_to<W: Write>(&self, writer: &mut W) -> PersistenceResult<()> {
    match self {
      None => {
        b'N'.save_to(writer)?;
      },
      Some(value) => {
        b'S'.save_to(writer)?;
        value.save_to(writer)?;
     }
    }
    Ok(())
  }
}




// Tests =====================================================================
#[cfg(test)]
mod tests {
  #[allow(unused_imports)]
  use super::*;
  
  #[test]
  fn a_test() {
    
  }
  
  #[test]
  #[should_panic]
  fn a_failure_test() {
    panic!()
  }
}