atomic-ops 0.1.1

Performs atomic operations in the filesystem
Documentation
pub mod constants;
pub mod file;

use crate::error::{OpsError, OpsResult};
use std::cell::RefCell;

#[derive(Clone, Copy, Debug)]
pub enum ProcStatus {
    INIT,
    PREPARED,
    RAN,
    CLEAN,
    REVERTED,
}

impl From<ProcStatus> for u8 {
    fn from(value: ProcStatus) -> Self {
        match value {
            ProcStatus::INIT => 0,
            ProcStatus::PREPARED => 1,
            ProcStatus::RAN => 2,
            ProcStatus::CLEAN => 3,
            ProcStatus::REVERTED => 4,
        }
    }
}

impl TryFrom<u8> for ProcStatus {
    type Error = OpsError;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(ProcStatus::INIT),
            1 => Ok(ProcStatus::PREPARED),
            2 => Ok(ProcStatus::RAN),
            3 => Ok(ProcStatus::CLEAN),
            4 => Ok(ProcStatus::REVERTED),
            _ => Err(OpsError::SerializeFailed),
        }
    }
}

pub trait Process {
    fn prepare(&self) -> OpsResult<()>;
    fn run(&self) -> OpsResult<()>;
    fn clean(&self) -> OpsResult<()>;

    fn revert_prepare(&self) -> OpsResult<()>;
    fn revert_run(&self) -> OpsResult<()>;

    fn as_bytes(&self) -> &[u8];

    fn id() -> u8
    where
        Self: Sized;
}

pub struct BoxedProc {
    id: u8,
    offset: usize,
    status: RefCell<ProcStatus>,
    process: Box<dyn Process>,
}

pub const STATUS_SHIFT: usize = 5;
pub const BOXED_PROC_SIZE: usize = 6; // length(4) + status(1) + id(1)
impl BoxedProc {
    pub fn new(id: u8, process: Box<dyn Process>) -> Self {
        Self {
            id,
            offset: 0,
            status: RefCell::new(ProcStatus::INIT),
            process,
        }
    }

    pub fn new_with_offset(
        id: u8,
        process: Box<dyn Process>,
        status: ProcStatus,
        offset: usize,
    ) -> Self {
        Self {
            id,
            offset,
            status: RefCell::new(status),
            process,
        }
    }

    #[inline]
    pub fn prepare(&self) -> OpsResult<()> {
        println!("Prepare {}", self.id);
        let mut borrow = self
            .status
            .try_borrow_mut()
            .map_err(|_| OpsError::BorrowingStatusFailed)?;
        self.process.prepare()?;
        *borrow = ProcStatus::PREPARED;
        Ok(())
    }

    #[inline]
    pub fn run(&self) -> OpsResult<()> {
        println!("Run {}", self.id);
        let mut borrow = self
            .status
            .try_borrow_mut()
            .map_err(|_| OpsError::BorrowingStatusFailed)?;
        self.process.run()?;
        *borrow = ProcStatus::RAN;
        Ok(())
    }

    #[inline]
    pub fn clean(&self) -> OpsResult<()> {
        println!("Clean {}", self.id);
        let mut borrow = self
            .status
            .try_borrow_mut()
            .map_err(|_| OpsError::BorrowingStatusFailed)?;
        self.process.clean()?;
        *borrow = ProcStatus::CLEAN;
        Ok(())
    }

    #[inline]
    pub fn revert_prepare(&self) -> OpsResult<()> {
        println!("Revert PREPARE {}", self.id);
        self.process.revert_prepare()
    }

    #[inline]
    pub fn revert_run(&self) -> OpsResult<()> {
        println!("Revert RUN {}", self.id);
        self.process.revert_run()
    }

    #[inline]
    pub fn status(&self) -> ProcStatus {
        *self.status.borrow()
    }

    #[inline]
    pub fn offcet(&self) -> usize {
        self.offset
    }

    #[inline]
    pub fn id(&self) -> u8 {
        self.id
    }

    #[inline]
    pub fn set_offset(&mut self, offset: usize) {
        self.offset = offset;
    }

    #[inline]
    pub fn encode(&self) -> Vec<u8> {
        let process_bytes = self.process.as_bytes();
        let length = process_bytes.len();
        let status: u8 = self.status().into();
        let mut bytes = Vec::with_capacity(length);

        // max size of file is 4GB
        bytes.extend_from_slice((length as u32).to_le_bytes().as_slice());
        bytes.extend_from_slice(&[self.id]);
        bytes.extend_from_slice(&[status]);
        bytes.extend_from_slice(process_bytes);
        bytes
    }

    #[inline]
    pub fn print(&self) {
        println!("BoxedProc: {}, {:?}", self.id, self.status)
    }
}