lance_jni/
error.rs

1// Copyright 2024 Lance Developers.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::str::Utf8Error;
16
17use arrow_schema::ArrowError;
18use jni::{errors::Error as JniError, JNIEnv};
19use lance::error::Error as LanceError;
20use serde_json::Error as JsonError;
21
22#[derive(Debug, PartialEq, Eq)]
23pub enum JavaExceptionClass {
24    IllegalArgumentException,
25    IOException,
26    RuntimeException,
27    UnsupportedOperationException,
28    AlreadyInException,
29}
30
31impl JavaExceptionClass {
32    pub fn as_str(&self) -> &str {
33        match self {
34            Self::IllegalArgumentException => "java/lang/IllegalArgumentException",
35            Self::IOException => "java/io/IOException",
36            Self::RuntimeException => "java/lang/RuntimeException",
37            Self::UnsupportedOperationException => "java/lang/UnsupportedOperationException",
38            // Included for display purposes.  This is not a real exception.
39            Self::AlreadyInException => "AlreadyInException",
40        }
41    }
42}
43
44#[derive(Debug)]
45pub struct Error {
46    message: String,
47    java_class: JavaExceptionClass,
48}
49
50impl Error {
51    pub fn new(message: String, java_class: JavaExceptionClass) -> Self {
52        Self {
53            message,
54            java_class,
55        }
56    }
57
58    pub fn runtime_error(message: String) -> Self {
59        Self {
60            message,
61            java_class: JavaExceptionClass::RuntimeException,
62        }
63    }
64
65    pub fn io_error(message: String) -> Self {
66        Self::new(message, JavaExceptionClass::IOException)
67    }
68
69    pub fn input_error(message: String) -> Self {
70        Self::new(message, JavaExceptionClass::IllegalArgumentException)
71    }
72
73    pub fn unsupported_error(message: String) -> Self {
74        Self::new(message, JavaExceptionClass::UnsupportedOperationException)
75    }
76
77    pub fn in_exception() -> Self {
78        Self {
79            message: String::default(),
80            java_class: JavaExceptionClass::AlreadyInException,
81        }
82    }
83
84    pub fn throw(&self, env: &mut JNIEnv) {
85        if self.java_class == JavaExceptionClass::AlreadyInException {
86            // An exception is already in progress, so we don't need to throw another one.
87            return;
88        }
89        if let Err(e) = env.throw_new(self.java_class.as_str(), &self.message) {
90            eprintln!("Error when throwing Java exception: {:?}", e.to_string());
91            panic!("Error when throwing Java exception: {:?}", e);
92        }
93    }
94}
95
96pub type Result<T> = std::result::Result<T, Error>;
97
98impl std::fmt::Display for Error {
99    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100        write!(f, "{}: {}", self.java_class.as_str(), self.message)
101    }
102}
103
104impl From<LanceError> for Error {
105    fn from(err: LanceError) -> Self {
106        match err {
107            LanceError::DatasetNotFound { .. }
108            | LanceError::DatasetAlreadyExists { .. }
109            | LanceError::CommitConflict { .. }
110            | LanceError::InvalidInput { .. } => Self::input_error(err.to_string()),
111            LanceError::IO { .. } => Self::io_error(err.to_string()),
112            LanceError::NotSupported { .. } => Self::unsupported_error(err.to_string()),
113            LanceError::NotFound { .. } => Self::io_error(err.to_string()),
114            _ => Self::runtime_error(err.to_string()),
115        }
116    }
117}
118
119impl From<ArrowError> for Error {
120    fn from(err: ArrowError) -> Self {
121        match err {
122            ArrowError::InvalidArgumentError { .. } => Self::input_error(err.to_string()),
123            ArrowError::IoError { .. } => Self::io_error(err.to_string()),
124            ArrowError::NotYetImplemented(_) => Self::unsupported_error(err.to_string()),
125            _ => Self::runtime_error(err.to_string()),
126        }
127    }
128}
129
130impl From<JsonError> for Error {
131    fn from(err: JsonError) -> Self {
132        Self::io_error(err.to_string())
133    }
134}
135
136impl From<JniError> for Error {
137    fn from(err: JniError) -> Self {
138        match err {
139            // If we get this then it means that an exception was already in progress.  We can't
140            // throw another one so we just return an error indicating that.
141            JniError::JavaException => Self::in_exception(),
142            _ => Self::runtime_error(err.to_string()),
143        }
144    }
145}
146
147impl From<Utf8Error> for Error {
148    fn from(err: Utf8Error) -> Self {
149        Self::input_error(err.to_string())
150    }
151}