use crate::{FromValue, ToValue, Value};
#[derive(Debug)]
pub enum CamlError {
NotFound,
Failure(&'static str),
InvalidArgument(&'static str),
OutOfMemory,
StackOverflow,
SysError(&'static str),
EndOfFile,
ZeroDivide,
ArrayBoundError,
SysBlockedIo,
Exception(Value),
WithArg(Value, Value),
}
#[derive(Debug)]
pub enum Error {
NotCallable,
NotDoubleArray,
Message(&'static str),
#[cfg(not(feature = "no-std"))]
Error(Box<dyn std::error::Error>),
Caml(CamlError),
}
#[cfg(not(feature = "no-std"))]
impl<T: 'static + std::error::Error> From<T> for Error {
fn from(x: T) -> Error {
Error::Error(Box::new(x))
}
}
impl From<CamlError> for Error {
fn from(x: CamlError) -> Error {
Error::Caml(x)
}
}
impl Error {
pub fn reraise(exc: Value) -> Result<(), Error> {
Err(CamlError::Exception(exc).into())
}
pub fn raise<S: AsRef<str>>(exc: S) -> Result<(), Error> {
let value = match Value::named(exc.as_ref()) {
Some(v) => v,
None => {
return Err(Error::Message(
"Value has not been registered with the OCaml runtime",
))
}
};
Err(CamlError::Exception(value).into())
}
pub fn raise_with_arg<S: AsRef<str>, T: ToValue>(exc: S, arg: T) -> Result<(), Error> {
let value = match Value::named(exc.as_ref()) {
Some(v) => v,
None => {
return Err(Error::Message(
"Value has not been registered with the OCaml runtime",
))
}
};
Err(CamlError::WithArg(value, arg.to_value()).into())
}
pub fn not_found() -> Result<(), Error> {
Err(CamlError::NotFound.into())
}
pub fn out_of_memory() -> Result<(), Error> {
Err(CamlError::OutOfMemory.into())
}
pub fn failwith(s: &'static str) -> Result<(), Error> {
Err(CamlError::Failure(s).into())
}
pub fn invalid_argument(s: &'static str) -> Result<(), Error> {
Err(CamlError::Failure(s).into())
}
#[doc(hidden)]
pub fn raise_failure(s: &str) -> ! {
let s = s.to_value();
unsafe {
crate::sys::caml_failwith_value(s.0);
}
#[allow(clippy::empty_loop)]
loop {}
}
#[doc(hidden)]
pub fn raise_value(v: Value, s: &str) -> ! {
let s = s.to_value();
unsafe {
crate::sys::caml_raise_with_arg(v.0, s.0);
}
#[allow(clippy::empty_loop)]
loop {}
}
pub fn named<S: AsRef<str>>(s: S) -> Option<Value> {
Value::named(s.as_ref())
}
}
#[cfg(not(feature = "no-std"))]
unsafe impl<T: ToValue, E: 'static + std::error::Error> ToValue for Result<T, E> {
fn to_value(self) -> Value {
match self {
Ok(x) => x.to_value(),
Err(y) => {
let e: Result<T, Error> = Err(Error::Error(Box::new(y)));
e.to_value()
}
}
}
}
unsafe impl<T: ToValue> ToValue for Result<T, Error> {
fn to_value(self) -> Value {
match self {
Ok(x) => return x.to_value(),
Err(Error::Caml(CamlError::Exception(e))) => unsafe {
crate::sys::caml_raise(e.0);
},
Err(Error::Caml(CamlError::NotFound)) => unsafe {
crate::sys::caml_raise_not_found();
},
Err(Error::Caml(CamlError::ArrayBoundError)) => unsafe {
crate::sys::caml_array_bound_error();
},
Err(Error::Caml(CamlError::OutOfMemory)) => unsafe {
crate::sys::caml_array_bound_error();
},
Err(Error::Caml(CamlError::EndOfFile)) => unsafe {
crate::sys::caml_raise_end_of_file()
},
Err(Error::Caml(CamlError::StackOverflow)) => unsafe {
crate::sys::caml_raise_stack_overflow()
},
Err(Error::Caml(CamlError::ZeroDivide)) => unsafe {
crate::sys::caml_raise_zero_divide()
},
Err(Error::Caml(CamlError::SysBlockedIo)) => unsafe {
crate::sys::caml_raise_sys_blocked_io()
},
Err(Error::Caml(CamlError::InvalidArgument(s))) => {
unsafe {
let s = crate::util::CString::new(s).expect("Invalid C string");
crate::sys::caml_invalid_argument(s.as_ptr() as *const ocaml_sys::Char)
};
}
Err(Error::Caml(CamlError::WithArg(a, b))) => unsafe {
crate::sys::caml_raise_with_arg(a.0, b.0)
},
Err(Error::Caml(CamlError::SysError(s))) => {
unsafe {
let s = s.to_value();
crate::sys::caml_raise_sys_error(s.0)
};
}
Err(Error::Message(s)) => {
unsafe {
let s = crate::util::CString::new(s).expect("Invalid C string");
crate::sys::caml_failwith(s.as_ptr() as *const ocaml_sys::Char)
};
}
Err(Error::Caml(CamlError::Failure(s))) => {
unsafe {
let s = crate::util::CString::new(s).expect("Invalid C string");
crate::sys::caml_failwith(s.as_ptr() as *const ocaml_sys::Char)
};
}
#[cfg(not(feature = "no-std"))]
Err(Error::Error(e)) => {
let s = format!("{:?}\0", e);
unsafe { crate::sys::caml_failwith(s.as_ptr() as *const ocaml_sys::Char) };
}
Err(Error::NotDoubleArray) => {
let s = "invalid double array\0";
unsafe { crate::sys::caml_failwith(s.as_ptr() as *const ocaml_sys::Char) };
}
Err(Error::NotCallable) => {
let s = "value is not callable\0";
unsafe { crate::sys::caml_failwith(s.as_ptr() as *const ocaml_sys::Char) };
}
};
Value::unit()
}
}
unsafe impl<T: FromValue> FromValue for Result<T, crate::Error> {
fn from_value(value: Value) -> Result<T, crate::Error> {
if value.is_exception_result() {
return Err(CamlError::Exception(value.exception().unwrap()).into());
}
Ok(T::from_value(value))
}
}