use crate::{FromValue, Runtime, ToValue, Value};
#[derive(Debug)]
pub enum CamlError {
NotFound,
Failure(&'static str),
InvalidArgument(&'static str),
OutOfMemory,
StackOverflow,
SysError(Value),
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 unsafe { 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>>(exc: S, arg: Value) -> Result<(), Error> {
let value = match unsafe { 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).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) -> ! {
unsafe {
let value = crate::sys::caml_alloc_string(s.len());
let ptr = crate::sys::string_val(value);
core::ptr::copy_nonoverlapping(s.as_ptr(), ptr, s.len());
crate::sys::caml_failwith_value(value);
}
#[allow(clippy::empty_loop)]
loop {}
}
#[doc(hidden)]
pub fn raise_value(v: Value, s: &str) -> ! {
unsafe {
let st = crate::sys::caml_alloc_string(s.len());
let ptr = crate::sys::string_val(st);
core::ptr::copy_nonoverlapping(s.as_ptr(), ptr, s.len());
crate::sys::caml_raise_with_arg(v.raw().0, st);
}
#[allow(clippy::empty_loop)]
loop {}
}
pub fn named<S: AsRef<str>>(s: S) -> Option<Value> {
unsafe { Value::named(s.as_ref()) }
}
}
unsafe impl<T: ToValue, E: ToValue> ToValue for Result<T, E> {
fn to_value(&self, rt: &Runtime) -> Value {
unsafe {
match self {
Ok(x) => Value::result_ok(rt, x),
Err(e) => Value::result_error(rt, e),
}
}
}
}
unsafe impl<T: ToValue> ToValue for Result<T, Error> {
fn to_value(&self, rt: &Runtime) -> Value {
match self {
Ok(x) => return x.to_value(rt),
Err(Error::Caml(CamlError::Exception(e))) => unsafe {
crate::sys::caml_raise(e.raw().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_raise_out_of_memory();
},
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.raw().0, b.raw().0)
},
Err(Error::Caml(CamlError::SysError(s))) => {
unsafe { crate::sys::caml_raise_sys_error(s.raw().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) };
}
};
unreachable!()
}
}
unsafe impl<T: FromValue> FromValue for Result<T, crate::Error> {
fn from_value(value: Value) -> Result<T, crate::Error> {
unsafe {
if value.is_exception_result() {
return Err(CamlError::Exception(value).into());
}
Ok(T::from_value(value))
}
}
}
unsafe impl<A: FromValue, B: FromValue> FromValue for Result<A, B> {
fn from_value(value: Value) -> Result<A, B> {
unsafe { value.result() }
}
}