dvcompute 2.0.0

Discrete event simulation library (sequential simulation)
Documentation
// Copyright (c) 2020-2022  David Sorokin <davsor@mail.ru>, based in Yoshkar-Ola, Russia
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use std;
use std::rc::Rc;
use std::io;
use std::ops::Deref;
use std::fmt;
use std::convert::From;

#[cfg(feature="cons_mode")]
use std::ffi::CString;

#[cfg(feature="cons_mode")]
use std::ffi::CStr;

#[cfg(feature="cons_mode")]
use std::ptr;

#[cfg(feature="cons_mode")]
use libc::*;

/// Defines a possible non-standard situation that may occur within simulation.
#[doc(hidden)]
#[derive(Debug, Clone)]
pub enum Error {

    /// The computation was canceled.
    Cancel,

    /// Another error as a trade-off between speed and generality.
    Other(Rc<OtherError>)
}

/// Another error that may arise during simulation.
#[doc(hidden)]
#[derive(Debug)]
pub enum OtherError {

    /// The computation should be retried again.
    Retry(String),

    /// Some panic error.
    Panic(String),

    /// The Input / Output error.
    IO(io::Error)
}

#[doc(hidden)]
impl Error {

    /// Merge two errors.
    #[inline]
    pub fn merge(&self, other: &Error) -> Error {
        match (self, other) {
            (&Error::Cancel, &Error::Cancel) => {
                Error::Cancel
            },
            (&Error::Cancel, &Error::Other(ref y)) => {
                match y.deref() {
                    &OtherError::Retry(_) => Error::Other(y.clone()),
                    _ => Error::Cancel
                }
            },
            (&Error::Other(ref x), &Error::Cancel) => {
                match x.deref() {
                    &OtherError::Retry(_) => Error::Other(x.clone()),
                    _ => Error::Cancel
                }
            },
            (&Error::Other(ref x), &Error::Other(ref y)) => {
                match (x.deref(), y.deref()) {
                    (&OtherError::Retry(_), _) => Error::Other(x.clone()),
                    (_, &OtherError::Retry(_)) => Error::Other(y.clone()),
                    (_, _) => Error::Other(x.clone())
                }
            }
        }
    }

    /// Create a retry error.
    #[inline]
    pub fn retry(msg: String) -> Self {
        Error::Other(OtherError::retry(msg))
    }

    /// Create a panic error.
    #[inline]
    pub fn panic(msg: String) -> Self {
        Error::Other(OtherError::panic(msg))
    }

    /// Create the IO error.
    #[inline]
    pub fn io(err: io::Error) -> Self {
        Error::Other(OtherError::io(err))
    }
}

#[doc(hidden)]
impl OtherError {

    /// Create a retry error.
    #[inline]
    pub fn retry(msg: String) -> Rc<Self> {
        Rc::new(OtherError::Retry(msg))
    }

    /// Create a panic error.
    #[inline]
    pub fn panic(msg: String) -> Rc<Self> {
        Rc::new(OtherError::Panic(msg))
    }

    /// Create the IO error.
    #[inline]
    pub fn io(err: io::Error) -> Rc<Self> {
        Rc::new(OtherError::IO(err))
    }
}

impl fmt::Display for Error {

    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self {
            &Error::Cancel => write!(f, "Computation was canceled"),
            &Error::Other(ref x) => x.fmt(f)
        }
    }
}

impl fmt::Display for OtherError {

    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self {
            &OtherError::Retry(ref x) => write!(f, "{}", x),
            &OtherError::Panic(ref x) => write!(f, "{}", x),
            &OtherError::IO(ref x) => x.fmt(f)
        }
    }
}

impl std::error::Error for Error {}

impl std::error::Error for OtherError {}

impl From<io::Error> for Error {

    fn from(e: io::Error) -> Error {
        Error::io(e)
    }
}

/// The computation was cancelled.
#[cfg(feature="cons_mode")]
const CANCEL_STATUS: c_int = 1;

/// The computation must be retried.
#[cfg(feature="cons_mode")]
const RETRY_STATUS: c_int = 2;

/// There was a panic situation.
#[cfg(feature="cons_mode")]
const PANIC_STATUS: c_int = 3;

/// There was an IO error.
#[cfg(feature="cons_mode")]
const IO_ERROR_STATUS: c_int = 4;

/// A C-friendly representation of the error.
#[cfg(feature="cons_mode")]
#[repr(C)]
pub struct ErrorRepr {

    /// The status of the error.
    status: c_int,

    /// The description of the error message.
    message: *mut c_char,

    /// Delete the message.
    delete_message: unsafe extern "C" fn(*mut c_char),

    /// Delete the error representation.
    delete_error_repr: unsafe extern "C" fn(*mut ErrorRepr)
}

#[cfg(feature="cons_mode")]
impl Drop for ErrorRepr {

    fn drop(&mut self) {
        unsafe {
            (self.delete_message)(self.message);
        }
    }
}

#[cfg(feature="cons_mode")]
impl ErrorRepr {

    /// Create a new C-friendly error representation.
    pub fn new(e: Error) -> Self {
        let (status, message) = {
            match e {
                Error::Cancel => (CANCEL_STATUS, ptr::null_mut()),
                Error::Other(x) => {
                    match x.deref() {
                        &OtherError::Retry(ref x) => {
                            let message = x.clone();
                            let message = CString::new(message).expect("NulError");
                            let message = CString::into_raw(message);
                            (RETRY_STATUS, message)
                        },
                        &OtherError::Panic(ref x) => {
                            let message = x.clone();
                            let message = CString::new(message).expect("NulError");
                            let message = CString::into_raw(message);
                            (PANIC_STATUS, message)
                        }
                        &OtherError::IO(ref x) => {
                            let message = format!("{}", x);
                            let message = CString::new(message).expect("NulError");
                            let message = CString::into_raw(message);
                            (IO_ERROR_STATUS, message)
                        }
                    }
                }
            }
        };

        ErrorRepr {
            status: status,
            message: message,
            delete_message: delete_error_message,
            delete_error_repr: delete_error_repr
        }
    }
}

#[cfg(feature="cons_mode")]
unsafe extern "C" fn delete_error_message(message: *mut c_char) {
    if message != ptr::null_mut() {
        let _ = CString::from_raw(message);
    }
}

#[cfg(feature="cons_mode")]
unsafe extern "C" fn delete_error_repr(e: *mut ErrorRepr) {
    if e != ptr::null_mut() {
        let _ = Box::from_raw(e);
    }
}

/// Convert the `ErrorRepr` referenced to by the FFI pointer to an `Error`.
#[cfg(feature="cons_mode")]
pub unsafe fn ffi_error_repr_into_error(e: *mut ErrorRepr) -> Error {
    if (*e).status == CANCEL_STATUS {
        ((*e).delete_error_repr)(e);
        Error::Cancel
    } else {
        let message = CStr::from_ptr((*e).message);
        let message = message.to_bytes().to_vec();
        let message = String::from_utf8(message).expect("FromUtf8Error");
        let status  = (*e).status;
        ((*e).delete_error_repr)(e);
        match status {
            RETRY_STATUS => {
                Error::Other(Rc::new(OtherError::Retry(message)))
            },
            PANIC_STATUS => {
                Error::Other(Rc::new(OtherError::Panic(message)))
            },
            IO_ERROR_STATUS => {
                let e = io::Error::new(io::ErrorKind::Other, message);
                Error::Other(Rc::new(OtherError::IO(e)))
            },
            x => {
                panic!("Unexpected error status: {}", x);
            }
        }
    }
}