dvcompute/simulation/
error.rs

1// Copyright (c) 2020-2022  David Sorokin <davsor@mail.ru>, based in Yoshkar-Ola, Russia
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
7use std;
8use std::rc::Rc;
9use std::io;
10use std::ops::Deref;
11use std::fmt;
12use std::convert::From;
13
14#[cfg(feature="cons_mode")]
15use std::ffi::CString;
16
17#[cfg(feature="cons_mode")]
18use std::ffi::CStr;
19
20#[cfg(feature="cons_mode")]
21use std::ptr;
22
23#[cfg(feature="cons_mode")]
24use libc::*;
25
26/// Defines a possible non-standard situation that may occur within simulation.
27#[doc(hidden)]
28#[derive(Debug, Clone)]
29pub enum Error {
30
31    /// The computation was canceled.
32    Cancel,
33
34    /// Another error as a trade-off between speed and generality.
35    Other(Rc<OtherError>)
36}
37
38/// Another error that may arise during simulation.
39#[doc(hidden)]
40#[derive(Debug)]
41pub enum OtherError {
42
43    /// The computation should be retried again.
44    Retry(String),
45
46    /// Some panic error.
47    Panic(String),
48
49    /// The Input / Output error.
50    IO(io::Error)
51}
52
53#[doc(hidden)]
54impl Error {
55
56    /// Merge two errors.
57    #[inline]
58    pub fn merge(&self, other: &Error) -> Error {
59        match (self, other) {
60            (&Error::Cancel, &Error::Cancel) => {
61                Error::Cancel
62            },
63            (&Error::Cancel, &Error::Other(ref y)) => {
64                match y.deref() {
65                    &OtherError::Retry(_) => Error::Other(y.clone()),
66                    _ => Error::Cancel
67                }
68            },
69            (&Error::Other(ref x), &Error::Cancel) => {
70                match x.deref() {
71                    &OtherError::Retry(_) => Error::Other(x.clone()),
72                    _ => Error::Cancel
73                }
74            },
75            (&Error::Other(ref x), &Error::Other(ref y)) => {
76                match (x.deref(), y.deref()) {
77                    (&OtherError::Retry(_), _) => Error::Other(x.clone()),
78                    (_, &OtherError::Retry(_)) => Error::Other(y.clone()),
79                    (_, _) => Error::Other(x.clone())
80                }
81            }
82        }
83    }
84
85    /// Create a retry error.
86    #[inline]
87    pub fn retry(msg: String) -> Self {
88        Error::Other(OtherError::retry(msg))
89    }
90
91    /// Create a panic error.
92    #[inline]
93    pub fn panic(msg: String) -> Self {
94        Error::Other(OtherError::panic(msg))
95    }
96
97    /// Create the IO error.
98    #[inline]
99    pub fn io(err: io::Error) -> Self {
100        Error::Other(OtherError::io(err))
101    }
102}
103
104#[doc(hidden)]
105impl OtherError {
106
107    /// Create a retry error.
108    #[inline]
109    pub fn retry(msg: String) -> Rc<Self> {
110        Rc::new(OtherError::Retry(msg))
111    }
112
113    /// Create a panic error.
114    #[inline]
115    pub fn panic(msg: String) -> Rc<Self> {
116        Rc::new(OtherError::Panic(msg))
117    }
118
119    /// Create the IO error.
120    #[inline]
121    pub fn io(err: io::Error) -> Rc<Self> {
122        Rc::new(OtherError::IO(err))
123    }
124}
125
126impl fmt::Display for Error {
127
128    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
129        match self {
130            &Error::Cancel => write!(f, "Computation was canceled"),
131            &Error::Other(ref x) => x.fmt(f)
132        }
133    }
134}
135
136impl fmt::Display for OtherError {
137
138    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
139        match self {
140            &OtherError::Retry(ref x) => write!(f, "{}", x),
141            &OtherError::Panic(ref x) => write!(f, "{}", x),
142            &OtherError::IO(ref x) => x.fmt(f)
143        }
144    }
145}
146
147impl std::error::Error for Error {}
148
149impl std::error::Error for OtherError {}
150
151impl From<io::Error> for Error {
152
153    fn from(e: io::Error) -> Error {
154        Error::io(e)
155    }
156}
157
158/// The computation was cancelled.
159#[cfg(feature="cons_mode")]
160const CANCEL_STATUS: c_int = 1;
161
162/// The computation must be retried.
163#[cfg(feature="cons_mode")]
164const RETRY_STATUS: c_int = 2;
165
166/// There was a panic situation.
167#[cfg(feature="cons_mode")]
168const PANIC_STATUS: c_int = 3;
169
170/// There was an IO error.
171#[cfg(feature="cons_mode")]
172const IO_ERROR_STATUS: c_int = 4;
173
174/// A C-friendly representation of the error.
175#[cfg(feature="cons_mode")]
176#[repr(C)]
177pub struct ErrorRepr {
178
179    /// The status of the error.
180    status: c_int,
181
182    /// The description of the error message.
183    message: *mut c_char,
184
185    /// Delete the message.
186    delete_message: unsafe extern "C" fn(*mut c_char),
187
188    /// Delete the error representation.
189    delete_error_repr: unsafe extern "C" fn(*mut ErrorRepr)
190}
191
192#[cfg(feature="cons_mode")]
193impl Drop for ErrorRepr {
194
195    fn drop(&mut self) {
196        unsafe {
197            (self.delete_message)(self.message);
198        }
199    }
200}
201
202#[cfg(feature="cons_mode")]
203impl ErrorRepr {
204
205    /// Create a new C-friendly error representation.
206    pub fn new(e: Error) -> Self {
207        let (status, message) = {
208            match e {
209                Error::Cancel => (CANCEL_STATUS, ptr::null_mut()),
210                Error::Other(x) => {
211                    match x.deref() {
212                        &OtherError::Retry(ref x) => {
213                            let message = x.clone();
214                            let message = CString::new(message).expect("NulError");
215                            let message = CString::into_raw(message);
216                            (RETRY_STATUS, message)
217                        },
218                        &OtherError::Panic(ref x) => {
219                            let message = x.clone();
220                            let message = CString::new(message).expect("NulError");
221                            let message = CString::into_raw(message);
222                            (PANIC_STATUS, message)
223                        }
224                        &OtherError::IO(ref x) => {
225                            let message = format!("{}", x);
226                            let message = CString::new(message).expect("NulError");
227                            let message = CString::into_raw(message);
228                            (IO_ERROR_STATUS, message)
229                        }
230                    }
231                }
232            }
233        };
234
235        ErrorRepr {
236            status: status,
237            message: message,
238            delete_message: delete_error_message,
239            delete_error_repr: delete_error_repr
240        }
241    }
242}
243
244#[cfg(feature="cons_mode")]
245unsafe extern "C" fn delete_error_message(message: *mut c_char) {
246    if message != ptr::null_mut() {
247        let _ = CString::from_raw(message);
248    }
249}
250
251#[cfg(feature="cons_mode")]
252unsafe extern "C" fn delete_error_repr(e: *mut ErrorRepr) {
253    if e != ptr::null_mut() {
254        let _ = Box::from_raw(e);
255    }
256}
257
258/// Convert the `ErrorRepr` referenced to by the FFI pointer to an `Error`.
259#[cfg(feature="cons_mode")]
260pub unsafe fn ffi_error_repr_into_error(e: *mut ErrorRepr) -> Error {
261    if (*e).status == CANCEL_STATUS {
262        ((*e).delete_error_repr)(e);
263        Error::Cancel
264    } else {
265        let message = CStr::from_ptr((*e).message);
266        let message = message.to_bytes().to_vec();
267        let message = String::from_utf8(message).expect("FromUtf8Error");
268        let status  = (*e).status;
269        ((*e).delete_error_repr)(e);
270        match status {
271            RETRY_STATUS => {
272                Error::Other(Rc::new(OtherError::Retry(message)))
273            },
274            PANIC_STATUS => {
275                Error::Other(Rc::new(OtherError::Panic(message)))
276            },
277            IO_ERROR_STATUS => {
278                let e = io::Error::new(io::ErrorKind::Other, message);
279                Error::Other(Rc::new(OtherError::IO(e)))
280            },
281            x => {
282                panic!("Unexpected error status: {}", x);
283            }
284        }
285    }
286}