toad_jni/java/lang/
throwable.rs

1use java::NoUpcast;
2
3use crate::java::{self, Nullable, Object};
4
5/// `java.lang.Throwable`
6pub struct StackTraceElement(java::lang::Object);
7java::object_newtype!(StackTraceElement);
8impl java::Class for StackTraceElement {
9  const PATH: &'static str = "java/lang/StackTraceElement";
10}
11
12/// `java.lang.Throwable`
13pub struct Throwable(java::lang::Object);
14
15java::object_newtype!(Throwable);
16impl java::Class for Throwable {
17  const PATH: &'static str = "java/lang/Throwable";
18}
19
20impl Throwable {
21  /// `java.lang.Throwable.getStackTrace()`
22  pub fn stack_trace(&self, e: &mut java::Env) -> Vec<StackTraceElement> {
23    java::Method::<Self, fn() -> Vec<StackTraceElement>>::new("getStackTrace").invoke(e, self)
24  }
25
26  /// `java.lang.Throwable.getCause()`
27  pub fn cause(&self, e: &mut java::Env) -> Option<Throwable> {
28    java::Method::<Self, fn() -> Nullable<Throwable>>::new("getCause").invoke(e, self)
29                                                                      .into_option(e)
30  }
31
32  /// Recursively travel up the `Throwable` cause chain until one has no inner exception
33  pub fn cause_iter(&self, e: &mut java::Env) -> impl Iterator<Item = Throwable> {
34    struct Iter(Throwable);
35    impl Iterator for Iter {
36      type Item = Throwable;
37
38      fn next(&mut self) -> Option<Self::Item> {
39        let mut e = java::env();
40        let e = &mut e;
41        let c = self.0.cause(e);
42        if let Some(c) = c.as_ref() {
43          self.0 = c.downcast_ref(e).upcast_to(e);
44        }
45        c
46      }
47    }
48
49    Iter(self.downcast_ref(e).upcast_to(e))
50  }
51}
52
53impl core::fmt::Debug for Throwable {
54  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55    let mut e = java::env();
56    let e = &mut e;
57    let traces = self.stack_trace(e);
58    let traces = traces.into_iter()
59                       .map(|o| o.downcast(e).to_string(e))
60                       .collect::<Vec<_>>();
61    write!(f, "{}\n", self.downcast_ref(e).to_string(e))?;
62    self.cause_iter(e)
63        .try_for_each(|cause| write!(f, "    {}\n", cause.downcast_ref(e).to_string(e)))?;
64    write!(f, "\nstacktrace:\n{:#?}", traces)?;
65
66    Ok(())
67  }
68}
69
70#[cfg(test)]
71mod tests {
72  use crate::java::io::IOException;
73
74  #[test]
75  fn dbg() {
76    let mut e = crate::test::init();
77    let e = &mut e;
78    let baz = IOException::new(e, "baz").to_throwable(e);
79    let bar = IOException::new_caused_by(e, "bar", baz).to_throwable(e);
80    let foo = IOException::new_caused_by(e, "foo", bar).to_throwable(e);
81
82    assert_eq!(
83               format!("{:?}", foo),
84               format!(
85      r#"
86java.io.IOException: foo
87    java.io.IOException: bar
88    java.io.IOException: baz
89
90stacktrace:
91{:#?}"#,
92      Vec::<String>::new()
93    ).trim_start()
94    );
95  }
96}