1use std::{backtrace::Backtrace, fmt::Debug, rc::Rc};
2
3use crate::prelude::IntoJavaType;
4
5#[derive(Debug, Clone)]
6pub enum JExceptionClass {
7 RuntimeException,
8 ArithmeticException,
9 ArrayIndexOutOfBoundsException,
10 ArrayStoreException,
11 ClassCastException,
12 IllegalArgumentException,
13 IllegalMonitorStateException,
14 IllegalStateException,
15 IllegalThreadStateException,
16 IndexOutOfBoundsException,
17 NegativeArraySizeException,
18 NullPointerException,
19 NumberFormatException,
20 SecurityException,
21 StringIndexOutOfBounds,
22 UnsupportedOperationException,
23 ClassNotFoundException,
24 CloneNotSupportedException,
25 IllegalAccessException,
26 InstantiationException,
27 InterruptedException,
28 NoSuchFieldException,
29 NoSuchMethodException,
30}
31
32impl JExceptionClass {
33 pub fn get_class_path(&self) -> String {
34 format!("java/lang/{:?}", self)
35 }
36}
37
38impl From<&jni::errors::Error> for JExceptionClass {
40 fn from(value: &jni::errors::Error) -> Self {
41 match value {
42 jni::errors::Error::WrongJValueType(_, _) => JExceptionClass::ClassCastException,
43 jni::errors::Error::InvalidCtorReturn => JExceptionClass::IllegalArgumentException,
44 jni::errors::Error::InvalidArgList(_) => JExceptionClass::IllegalArgumentException,
45 jni::errors::Error::MethodNotFound { name: _, sig: _ } => {
46 JExceptionClass::NoSuchMethodException
47 }
48 jni::errors::Error::FieldNotFound { name: _, sig: _ } => {
49 JExceptionClass::NoSuchFieldException
50 }
51 jni::errors::Error::JavaException => JExceptionClass::RuntimeException,
52 jni::errors::Error::JNIEnvMethodNotFound(_) => JExceptionClass::NoSuchMethodException,
53 jni::errors::Error::NullPtr(_) => JExceptionClass::NullPointerException,
54 jni::errors::Error::NullDeref(_) => JExceptionClass::NullPointerException,
55 jni::errors::Error::TryLock => JExceptionClass::IllegalStateException,
56 jni::errors::Error::JavaVMMethodNotFound(_) => JExceptionClass::NoSuchMethodException,
57 jni::errors::Error::FieldAlreadySet(_) => JExceptionClass::IllegalStateException,
58 jni::errors::Error::ThrowFailed(_) => JExceptionClass::IllegalStateException,
59 jni::errors::Error::ParseFailed(_, _) => JExceptionClass::IllegalStateException,
60 jni::errors::Error::JniCall(_) => JExceptionClass::UnsupportedOperationException,
61 }
62 }
63}
64
65impl std::fmt::Display for JExceptionClass {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 write!(f, "{:?}", self)
68 }
69}
70
71impl From<JExceptionClass> for JException {
72 fn from(val: JExceptionClass) -> Self {
73 let msg = &format!("{}", val);
74 JException::from_class_and_msg(val, msg)
75 }
76}
77
78#[derive(Clone)]
79pub struct JException {
80 pub class: JExceptionClass,
81 pub error: Rc<dyn std::error::Error>,
82}
83
84impl JException {
85 pub fn from_class_and_msg(class: JExceptionClass, msg: &str) -> Self {
86 let error: Box<dyn std::error::Error> = msg.to_string().into();
87 let error = Rc::<dyn std::error::Error>::from(error);
88 Self { class, error }
89 }
90
91 pub fn from_std<E: std::error::Error + 'static>(error: E) -> Self {
92 Self {
93 class: JExceptionClass::RuntimeException,
94 error: Rc::new(error),
95 }
96 }
97
98 pub fn from_std_with_class<E: std::error::Error + 'static>(
99 error: E,
100 j_class: JExceptionClass,
101 ) -> Self {
102 Self {
103 class: j_class,
104 error: Rc::new(error),
105 }
106 }
107}
108
109impl jni::errors::ToException for JException {
110 fn to_exception(&self) -> jni::errors::Exception {
111 jni::errors::Exception {
112 class: self.class.get_class_path(),
113 msg: format!("{}", self.error),
114 }
115 }
116}
117
118impl<E> From<E> for JException
119where
120 E: std::error::Error + 'static,
121{
122 fn from(error: E) -> Self {
123 JException::from_std(error)
124 }
125}
126
127impl std::fmt::Display for JException {
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 write!(f, "{}", self.error)
130 }
131}
132
133impl std::fmt::Debug for JException {
134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135 write!(f, "{}", self.error)
136 }
137}
138
139pub type JResult<T, E = JException> = core::result::Result<T, E>;
140
141pub fn j_result_handler<'a, T, R: Default>(
142 result: JResult<T, JException>,
143 env: &mut jni::JNIEnv<'a>,
144) -> R
145where
146 T: IntoJavaType<'a, R> + Default,
147{
148 match result {
149 Ok(ok) => match ok.into_java(env) {
150 Ok(ok) => ok,
151 Err(err) => {
152 env.j_throw_exception(err);
153 Default::default()
154 }
155 },
156 Err(err) => {
157 env.j_throw_exception(err);
158 Default::default()
159 }
160 }
161}
162
163pub fn option_handler<'a, T, R: Default>(
164 result: Option<T>,
165 env: &mut jni::JNIEnv<'a>,
166) -> R
167where
168 T: IntoJavaType<'a, R> + Default,
169{
170 match result {
171 Some(ok) => match ok.into_java(env) {
172 Ok(ok) => ok,
173 Err(err) => {
174 env.j_throw_exception(err);
175 Default::default()
176 }
177 }
178 None => Default::default()
179 }
180}
181
182macro_rules! jthrow {
185 ( $env:expr => $j_class:expr , $message:tt) => {
186 let error = $env.exception_occurred().unwrap_or_default();
187 if error.is_null() {
188 let message = format!(
189 "\nRust Error: {}\nRust Backtrace:\n{}\n",
190 &$message,
191 Backtrace::force_capture()
192 );
193 $env.throw_new(($j_class).get_class_path(), &message).ok();
194 }
195 };
196}
197
198#[allow(non_snake_case)]
199pub trait JNIEnvUtils {
200 fn j_throw_cause(&mut self, j_class: JExceptionClass, cause: &impl std::error::Error);
201 fn j_throw(&mut self, j_class: JExceptionClass);
202 fn j_throw_msg(&mut self, message: &str);
203 fn j_throw_exception(&mut self, ex: JException);
204
205 fn get_string_owned(
206 &mut self,
207 jstring: &jni::objects::JString<'_>,
208 ) -> jni::errors::Result<String>;
209}
210
211impl<'local> JNIEnvUtils for jni::JNIEnv<'local> {
212 fn j_throw_msg(&mut self, message: &str) {
213 jthrow!( self => &JExceptionClass::RuntimeException, message);
214 }
215 fn j_throw_cause(&mut self, j_class: JExceptionClass, cause: &impl std::error::Error) {
216 jthrow!( self => j_class, cause);
217 }
218 fn j_throw(&mut self, j_class: JExceptionClass) {
219 jthrow!( self => j_class, j_class);
220 }
221 fn j_throw_exception(&mut self, ex: JException) {
222 let c = ex.class;
223 let e = ex.error;
224 jthrow!( self => c, e);
225 }
226
227 fn get_string_owned(
228 &mut self,
229 jstring: &jni::objects::JString<'_>,
230 ) -> jni::errors::Result<String> {
231 let string = self.get_string(jstring)?;
232 string
233 .to_str()
234 .map(|s| s.to_string())
235 .map_err(|_| jni::errors::Error::WrongJValueType("JString", "-"))
236 }
237}
238
239pub trait JavaCatch<'local, T> {
241 fn j_catch(self, env: &mut jni::JNIEnv<'local>) -> crate::JResult<T>;
242}
243
244impl<'local, T, E: std::error::Error + 'static> JavaCatch<'local, T> for JResult<T, E> {
245 fn j_catch(self, env: &mut jni::JNIEnv<'local>) -> crate::JResult<T> {
246 match self {
247 Ok(ok) => Ok(ok),
248 Err(err) => {
249 jthrow!(env => JExceptionClass::RuntimeException, err);
250 Err(JException::from_std(err))
251 }
252 }
253 }
254}
255
256pub trait JavaCatchINI<'local, T> {
257 fn j_catch_ini(self, env: &mut jni::JNIEnv<'local>, msg: &str) -> crate::JResult<T>;
258}
259
260impl<'local, T> JavaCatchINI<'local, T> for Result<T, jni::errors::Error> {
261 fn j_catch_ini(self, env: &mut jni::JNIEnv<'local>, msg: &str) -> crate::JResult<T> {
262 match self {
263 Ok(ok) => Ok(ok),
264 Err(err) => {
265 let exception = JExceptionClass::from(&err);
266 let message = format!("{err}\n Cause: {msg}");
267 jthrow!(env => exception, message);
268 Err(JException::from_class_and_msg(exception, &message))
269 }
270 }
271 }
272}
273
274#[cfg(test)]
275mod tests {
276 use crate as java_bindgen;
277 use crate::prelude::*;
278
279 #[allow(non_snake_case)]
280 fn user<'a>(_: &mut JNIEnv<'a>, _class: JClass<'_>) -> JResult<String> {
281 Ok("ok".to_string())
282 }
283
284 #[allow(unused_mut, non_snake_case)]
285 pub extern "system" fn Java_com_test_Lib1_user<'a>(
286 mut env: JNIEnv<'a>,
287 _class: JClass<'_>,
288 ) -> jni::objects::JString<'a> {
289 let r = user(&mut env, _class);
290 j_result_handler(r, &mut env)
291 }
292
293 #[test_jvm]
294 fn should_return_ok<'a>(
295 test_env: &mut JNIEnv<'a>,
296 env: JNIEnv<'a>,
297 class: JClass,
298 ) -> JResult<()> {
299 let result = Java_com_test_Lib1_user(env, class);
301 let result: String = result.into_rust(test_env)?;
303 assert_eq!(&result, "ok");
304 Ok(())
305 }
306}