use std::{
fmt::{Debug, Display},
result,
};
use thiserror::Error;
use crate::AsJRef;
use crate::{java::lang::Throwable, Java, Jvm, JvmOp, Local};
pub type LocalResult<'jvm, T> = result::Result<T, Error<Local<'jvm, Throwable>>>;
pub type Result<T> = result::Result<T, Error<Java<Throwable>>>;
#[derive(Error)]
pub enum Error<T: AsJRef<Throwable>> {
#[error("Java invocation threw: {}", try_extract_message(.0))]
Thrown(T),
#[error(
"slice was too long (`{0}`) to convert to a Java array, which are limited to `i32::MAX`"
)]
SliceTooLong(usize),
#[error("attempted to deref a null Java object pointer")]
NullDeref,
#[error("attempted to nest `Jvm::with` calls")]
NestedUsage,
#[error("JVM already exists")]
JvmAlreadyExists,
#[cfg(feature = "dylibjvm")]
#[error(transparent)]
UnableToLoadLibjvm(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
#[error("{0}")]
JvmInternal(String),
}
fn try_extract_message(exception: &impl AsJRef<Throwable>) -> String {
let result =
|| -> crate::Result<_> { exception.as_jref()?.to_string().assert_not_null().execute() };
result().unwrap_or_else(|err| format!("failed to get message: {}", err))
}
impl<T> Debug for Error<T>
where
T: AsJRef<Throwable>,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(self, f)
}
}
impl<'jvm> Error<Local<'jvm, Throwable>> {
pub fn into_global(self, jvm: &mut Jvm<'jvm>) -> Error<Java<Throwable>> {
match self {
Error::Thrown(t) => Error::Thrown(jvm.global(&t)),
Error::SliceTooLong(s) => Error::SliceTooLong(s),
Error::NullDeref => Error::NullDeref,
Error::NestedUsage => Error::NestedUsage,
Error::JvmAlreadyExists => Error::JvmAlreadyExists,
#[cfg(feature = "dylibjvm")]
Error::UnableToLoadLibjvm(e) => Error::UnableToLoadLibjvm(e),
Error::JvmInternal(m) => Error::JvmInternal(m),
}
}
}