Skip to main content

dxfeed_graal/
error.rs

1//! Errors of the API.
2
3use std::ffi::{CStr, CString};
4use std::fmt;
5use std::sync::Arc;
6
7/// The result of a fallible call.
8pub type Result<T, E = Error> = std::result::Result<T, E>;
9
10/// An error of an SDK call.
11#[derive(Debug, Clone)]
12#[non_exhaustive]
13pub enum Error {
14    /// The SDK threw a Java exception.
15    Java(JavaException),
16    /// A GraalVM isolate function failed with this code, e.g. the isolate could not be created or a thread could not
17    /// be attached.
18    Graal(i32),
19    /// An argument cannot be passed to the SDK, e.g. a string with a NUL byte.
20    InvalidArgument(String),
21    /// The SDK has been shut down, so nothing new can be created.
22    ShutDown,
23    /// The call is not allowed inside an event listener.
24    InCallback,
25    /// The wait for a result timed out, e.g. in [`Promise::wait`](crate::Promise::wait).
26    Timeout,
27    /// The isolate of the SDK has already been created, or its creation has failed, so its arguments cannot be set any
28    /// more ([`system::set_isolate_arguments`](crate::system::set_isolate_arguments)).
29    IsolateCreated,
30    /// A Rust output failed, e.g. the writer given to
31    /// [`InstrumentProfileWriter::write`](crate::ipf::InstrumentProfileWriter::write).
32    Io(Arc<std::io::Error>),
33}
34
35impl fmt::Display for Error {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        match self {
38            Error::Java(e) => e.fmt(f),
39            Error::Graal(code) => write!(f, "GraalVM isolate error {code}"),
40            Error::InvalidArgument(what) => write!(f, "invalid argument: {what}"),
41            Error::ShutDown => f.write_str("the SDK has been shut down"),
42            Error::InCallback => f.write_str("the call is not allowed inside an event listener"),
43            Error::Timeout => f.write_str("the wait timed out"),
44            Error::IsolateCreated => f.write_str("the isolate has already been created"),
45            Error::Io(_) => f.write_str("the output failed"),
46        }
47    }
48}
49
50impl std::error::Error for Error {
51    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
52        match self {
53            Error::Java(e) => e.source(),
54            Error::Io(e) => Some(&**e),
55            _ => None,
56        }
57    }
58}
59
60/// A copy of a Java exception thrown by the SDK.
61#[derive(Debug, Clone)]
62pub struct JavaException {
63    class_name: String,
64    message: String,
65    stack_trace: String,
66    cause: Option<Box<JavaException>>,
67}
68
69impl JavaException {
70    pub(crate) fn new(class_name: String, message: String, stack_trace: String, cause: Option<Box<Self>>) -> Self {
71        JavaException {
72            class_name,
73            message,
74            stack_trace,
75            cause,
76        }
77    }
78
79    /// An exception with only a message, for SDK failures that come without a Java exception.
80    pub(crate) fn from_message(message: &str) -> Self {
81        Self::new(String::new(), message.to_owned(), String::new(), None)
82    }
83
84    /// The exception used when the SDK returned NULL where it promises a value.
85    pub(crate) fn null_result() -> Self {
86        Self::from_message("the SDK returned NULL")
87    }
88
89    /// The exception used when the SDK failed to report its own exception.
90    pub(crate) fn lost() -> Self {
91        Self::from_message("exception lost")
92    }
93
94    /// The fully qualified Java class name, e.g. `java.lang.IllegalArgumentException`; empty if unknown.
95    pub fn get_class_name(&self) -> &str {
96        &self.class_name
97    }
98
99    /// The exception message; empty if it has none.
100    pub fn get_message(&self) -> &str {
101        &self.message
102    }
103
104    /// The Java stack trace as `printStackTrace` prints it, causes included; empty if unknown.
105    pub fn get_stack_trace(&self) -> &str {
106        &self.stack_trace
107    }
108
109    /// The cause of the exception, if any.
110    pub fn get_cause(&self) -> Option<&JavaException> {
111        self.cause.as_deref()
112    }
113}
114
115impl fmt::Display for JavaException {
116    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117        match (self.class_name.is_empty(), self.message.is_empty()) {
118            (false, false) => write!(f, "{}: {}", self.class_name, self.message),
119            (false, true) => f.write_str(&self.class_name),
120            _ => f.write_str(&self.message),
121        }
122    }
123}
124
125impl std::error::Error for JavaException {
126    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
127        self.cause.as_deref().map(|c| c as _)
128    }
129}
130
131/// A class of the Java exceptions that [`Promise::complete_exceptionally`](crate::Promise::complete_exceptionally)
132/// and [`Promise::failed`](crate::Promise::failed) create: the fixed set that the SDK creates without reflection.
133#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
134#[non_exhaustive]
135pub enum JavaExceptionClass {
136    /// `java.lang.RuntimeException`.
137    RuntimeException,
138    /// `java.lang.IllegalArgumentException`.
139    IllegalArgumentException,
140    /// `java.lang.IllegalStateException`.
141    IllegalStateException,
142    /// `java.lang.UnsupportedOperationException`.
143    UnsupportedOperationException,
144    /// `java.io.IOException`.
145    IOException,
146    /// `java.util.concurrent.CancellationException`. It fails a promise but does not cancel it:
147    /// [`Promise::is_cancelled`](crate::Promise::is_cancelled) stays `false`;
148    /// [`Promise::cancel`](crate::Promise::cancel) cancels a promise.
149    CancellationException,
150    /// `java.util.concurrent.TimeoutException`.
151    TimeoutException,
152}
153
154impl JavaExceptionClass {
155    /// The fully qualified name of the class, as [`JavaException::get_class_name`] returns it.
156    pub fn get_name(self) -> &'static str {
157        match self {
158            JavaExceptionClass::RuntimeException => "java.lang.RuntimeException",
159            JavaExceptionClass::IllegalArgumentException => "java.lang.IllegalArgumentException",
160            JavaExceptionClass::IllegalStateException => "java.lang.IllegalStateException",
161            JavaExceptionClass::UnsupportedOperationException => "java.lang.UnsupportedOperationException",
162            JavaExceptionClass::IOException => "java.io.IOException",
163            JavaExceptionClass::CancellationException => "java.util.concurrent.CancellationException",
164            JavaExceptionClass::TimeoutException => "java.util.concurrent.TimeoutException",
165        }
166    }
167
168    /// The name as a NUL-terminated string for the SDK.
169    pub(crate) fn c_name(self) -> &'static CStr {
170        match self {
171            JavaExceptionClass::RuntimeException => c"java.lang.RuntimeException",
172            JavaExceptionClass::IllegalArgumentException => c"java.lang.IllegalArgumentException",
173            JavaExceptionClass::IllegalStateException => c"java.lang.IllegalStateException",
174            JavaExceptionClass::UnsupportedOperationException => c"java.lang.UnsupportedOperationException",
175            JavaExceptionClass::IOException => c"java.io.IOException",
176            JavaExceptionClass::CancellationException => c"java.util.concurrent.CancellationException",
177            JavaExceptionClass::TimeoutException => c"java.util.concurrent.TimeoutException",
178        }
179    }
180}
181
182impl fmt::Display for JavaExceptionClass {
183    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184        f.write_str(self.get_name())
185    }
186}
187
188/// The size of an SDK list of `count` elements; [`Error::InvalidArgument`] if it does not fit in `i32`.
189pub(crate) fn list_size(count: usize, what: &str) -> Result<i32> {
190    i32::try_from(count).map_err(|_| Error::InvalidArgument(format!("too many {what}: {count}")))
191}
192
193/// Converts `s` for the SDK; `what` names the argument in the error.
194pub(crate) fn c_string(s: &str, what: &str) -> Result<CString> {
195    CString::new(s).map_err(|_| Error::InvalidArgument(format!("{what} contains a NUL byte")))
196}