Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Better error throwing #205

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: second-iteration
Choose a base branch
Loading
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions 39 docs/src/internal/error_throwing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Errors
Napi-rs provides only a way to throw a JavaScript `Error` object. Both DataStax and ScyllaDB drivers utilize multiple error classes to represent different issues.

## Errors in the DataStax driver
The DataStax driver utilizes error classes native to Node.js:
- `Error`,
- `TypeError`,
- `RangeError`,
- `ReferenceError`,
- `SyntaxError`,

custom error classes without additional arguments:
- `ArgumentError`,
- `AuthenticationError`,
- `DriverError`,
- `DriverInternalError`,
- `NotSupportedError`,

and custom error classes with additional arguments:
- `BusyConnectionError`,
- `NoHostAvailableError`,
- `OperationTimedOutError`,
- `ResponseError`.


Some of the errors arguments would be easy to pass by adding logic to the function implemented in [#118](https://github.com/scylladb-zpp-2024-javascript-driver/scylladb-javascript-driver/pull/118), but some of them are not easy to pass and even not easily available in the Rust driver. For example, `NoHostAvailableError` has `innerErrors` parameter, which is an object map containing the error message for each host. This is available in the Rust driver only when using query execution history, otherwise only the error for the last host is available.

## Errors in the ScyllaDB driver
The ScyllaDB driver has multiple custom error classes that can be found in `scylla/src/error.rs` file. Some types of errors are enums, nesting more specific errors inside.

## Comparison
There are multiple errors in ScyllaDB driver that cannot be easily mapped to the DataStax driver errors. Some of them are only relevant to ScyllaDB, because of different database and driver designs. In the DataStax driver, the errors are more generic and most of them are classified under `ResponseError` with `code` parameter used to identify the nature of the error (a list of error codes can be found in `lib/types/index.js` file).

# Possible solutions
## 1. Use the DataStax driver errors
Use the old architecture of the DataStax driver and throw existing errors. Adding support for passing additional parameters of the errors would need to be implemented. All Rust errors would need to be mapped, possibly losing some of the information or encountering an issue that two cases are treated as the same error in the Rust driver but different errors in the DataStax driver. However, this would keep the interface closer to the DataStax driver if some users are expecting specific error types.

## 2. Use the ScyllaDB driver errors
Use the ScyllaDB driver errors and throw them. This would require implementing all of the error classes in JavaScript, taking into account the fact that some of them are enums (probably by using class inheritance). This would also need some work to pass the parameters of the errors. This approach would lose less information about the errors and add support for ScyllaDB specific errors. However, this would make the interface of possible errors different from the DataStax driver, resulting in users needing to reimplement some of the error handling logic - especially if they are testing for specific error types.
63 changes: 63 additions & 0 deletions 63 src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use std::{
error::Error,
fmt::{self, Display},
};

use napi::Status;

/// Enum representing possible JavaScript error types.
/// Error, RangeError, ReferenceError, SyntaxError, TypeError
/// are native JavaScript error types and the rest are custom
/// Datastax driver error types.
pub enum ErrorType {
ArgumentError,
AuthenticationError,
BusyConnectionError, // TODO: Add suport for fields of this error
DriverError,
DriverInternalError,
NoHostAvailableError, // TODO: Add suport for fields of this error
NotSupportedError,
OperationTimedOutError, // TODO: Add suport for fields of this error
ResponseError, // TODO: Add suport for fields of this error
Error,
RangeError,
ReferenceError,
SyntaxError,
TypeError,
}

impl Display for ErrorType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
ErrorType::ArgumentError => "ArgumentError",
ErrorType::AuthenticationError => "AuthenticationError",
ErrorType::BusyConnectionError => "BusyConnectionError",
ErrorType::DriverError => "DriverError",
ErrorType::DriverInternalError => "DriverInternalError",
ErrorType::NoHostAvailableError => "NoHostAvailableError",
ErrorType::NotSupportedError => "NotSupportedError",
ErrorType::OperationTimedOutError => "OperationTimedOutError",
ErrorType::ResponseError => "ResponseError",
ErrorType::Error => "Error",
ErrorType::RangeError => "RangeError",
ErrorType::ReferenceError => "ReferenceError",
ErrorType::SyntaxError => "SyntaxError",
ErrorType::TypeError => "TypeError",
})
}
}

/// Convert rust error to napi::Error
pub(crate) fn err_to_napi<T: Error>(e: T) -> napi::Error {
js_error(e)
}

/// Create napi::Error from a message
pub(crate) fn js_error<T: Display>(e: T) -> napi::Error {
js_typed_error(e, ErrorType::Error)
}

/// Create napi::Error from a message and error type
pub(crate) fn js_typed_error<T: Display>(e: T, error_type: ErrorType) -> napi::Error {
napi::Error::new(Status::GenericFailure, format!("{}#{}", error_type, e))
}
1 change: 1 addition & 0 deletions 1 src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ extern crate napi_derive;

// Link other files
pub mod auth;
pub mod errors;
pub mod options;
pub mod paging;
pub mod requests;
Expand Down
2 changes: 1 addition & 1 deletion 2 src/paging.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use napi::bindgen_prelude::{Buffer, ToNapiValue};
use scylla::response::{PagingState, PagingStateResponse};

use crate::{result::QueryResultWrapper, utils::js_error};
use crate::{errors::js_error, result::QueryResultWrapper};

#[napi]
pub struct PagingStateWrapper {
Expand Down
3 changes: 2 additions & 1 deletion 3 src/requests/parameter_wrappers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ use napi::bindgen_prelude::{BigInt, Buffer};
use scylla::value::{Counter, CqlTimestamp, CqlTimeuuid, CqlValue, MaybeUnset};

use crate::{
errors::js_error,
types::{
duration::DurationWrapper, inet::InetAddressWrapper, local_date::LocalDateWrapper,
local_time::LocalTimeWrapper, uuid::UuidWrapper,
},
utils::{bigint_to_i64, js_error},
utils::bigint_to_i64,
};

#[napi]
Expand Down
2 changes: 1 addition & 1 deletion 2 src/result.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use std::sync::Arc;

use crate::{
errors::err_to_napi,
types::{
local_date::LocalDateWrapper,
time_uuid::TimeUuidWrapper,
type_wrappers::{ComplexType, CqlType, CqlTypeClass},
uuid::UuidWrapper,
},
utils::err_to_napi,
};
use napi::bindgen_prelude::{BigInt, Buffer, ToNapiValue};
use scylla::{
Expand Down
7 changes: 3 additions & 4 deletions 7 src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@ use scylla::statement::prepared::PreparedStatement;
use scylla::statement::{Consistency, SerialConsistency, Statement};
use scylla::value::{CqlValue, MaybeUnset};

use crate::errors::{err_to_napi, js_error};
use crate::options;
use crate::paging::{PagingResult, PagingStateWrapper};
use crate::requests::parameter_wrappers::MaybeUnsetQueryParameterWrapper;
use crate::requests::request::QueryOptionsWrapper;
use crate::utils::{bigint_to_i64, js_error};
use crate::{
requests::request::PreparedStatementWrapper, result::QueryResultWrapper, utils::err_to_napi,
};
use crate::utils::bigint_to_i64;
use crate::{requests::request::PreparedStatementWrapper, result::QueryResultWrapper};

const DEFAULT_CACHE_SIZE: u32 = 512;

Expand Down
2 changes: 1 addition & 1 deletion 2 src/tests/error_throwing_tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::utils::{js_typed_error, ErrorType};
use crate::errors::{js_typed_error, ErrorType};

#[napi]
/// Test function that throws specified error with a custom message.
Expand Down
7 changes: 4 additions & 3 deletions 7 src/tests/utils_tests.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use napi::bindgen_prelude::BigInt;

use crate::utils::{self, bigint_to_i64};
use crate::errors::js_error;
use crate::utils::bigint_to_i64;

#[napi]
pub fn tests_bigint_to_i64(value: BigInt, case_id: Option<i32>) -> napi::Result<()> {
let case_id = match case_id {
Some(case_id) => case_id,
None => {
return utils::bigint_to_i64(value, "Overflow expected").map(|_| ());
return bigint_to_i64(value, "Overflow expected").map(|_| ());
}
};

Expand All @@ -28,7 +29,7 @@ pub fn tests_bigint_to_i64(value: BigInt, case_id: Option<i32>) -> napi::Result<
if v == expected {
Ok(())
} else {
Err(utils::js_error(format!("Got {}, expected{}", v, expected)))
Err(js_error(format!("Got {}, expected{}", v, expected)))
}
}
Err(e) => Err(e),
Expand Down
2 changes: 1 addition & 1 deletion 2 src/types/inet.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::utils::{js_typed_error, ErrorType};
use crate::errors::{js_typed_error, ErrorType};
use napi::bindgen_prelude::{Buffer, BufferSlice};
use std::net::IpAddr;

Expand Down
3 changes: 2 additions & 1 deletion 3 src/types/local_date.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::utils::{js_error, CharCounter};
use crate::errors::js_error;
use crate::utils::CharCounter;
use regex::Regex;
use scylla::value::CqlDate;
use std::sync::LazyLock;
Expand Down
3 changes: 2 additions & 1 deletion 3 src/types/local_time.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::utils::{bigint_to_i64, js_error, CharCounter};
use crate::errors::js_error;
use crate::utils::{bigint_to_i64, CharCounter};
use napi::bindgen_prelude::BigInt;
use scylla::value::CqlTime;
use std::fmt::{self, Write};
Expand Down
2 changes: 1 addition & 1 deletion 2 src/types/type_hints.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::utils::js_error;
use crate::errors::js_error;

use super::type_wrappers::{ComplexType, CqlType};

Expand Down
66 changes: 3 additions & 63 deletions 66 src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,66 +1,6 @@
use std::{
error::Error,
fmt::{self, Display},
};

use napi::{bindgen_prelude::BigInt, Status};

/// Enum representing possible JavaScript error types.
/// Error, RangeError, ReferenceError, SyntaxError, TypeError
/// are native JavaScript error types and the rest are custom
/// Datastax driver error types.
pub enum ErrorType {
ArgumentError,
AuthenticationError,
BusyConnectionError, // TODO: Add suport for fields of this error
DriverError,
DriverInternalError,
NoHostAvailableError, // TODO: Add suport for fields of this error
NotSupportedError,
OperationTimedOutError, // TODO: Add suport for fields of this error
ResponseError, // TODO: Add suport for fields of this error
Error,
RangeError,
ReferenceError,
SyntaxError,
TypeError,
}

impl Display for ErrorType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
ErrorType::ArgumentError => "ArgumentError",
ErrorType::AuthenticationError => "AuthenticationError",
ErrorType::BusyConnectionError => "BusyConnectionError",
ErrorType::DriverError => "DriverError",
ErrorType::DriverInternalError => "DriverInternalError",
ErrorType::NoHostAvailableError => "NoHostAvailableError",
ErrorType::NotSupportedError => "NotSupportedError",
ErrorType::OperationTimedOutError => "OperationTimedOutError",
ErrorType::ResponseError => "ResponseError",
ErrorType::Error => "Error",
ErrorType::RangeError => "RangeError",
ErrorType::ReferenceError => "ReferenceError",
ErrorType::SyntaxError => "SyntaxError",
ErrorType::TypeError => "TypeError",
})
}
}

/// Convert rust error to napi::Error
pub(crate) fn err_to_napi<T: Error>(e: T) -> napi::Error {
js_error(e)
}

/// Create napi::Error from a message
pub(crate) fn js_error<T: Display>(e: T) -> napi::Error {
js_typed_error(e, ErrorType::Error)
}

/// Create napi::Error from a message and error type
pub(crate) fn js_typed_error<T: Display>(e: T, error_type: ErrorType) -> napi::Error {
napi::Error::new(Status::GenericFailure, format!("{}#{}", error_type, e))
}
use crate::errors::js_error;
use napi::bindgen_prelude::BigInt;
use std::fmt::{self, Display};

/// Convert napi bigint to i64. Returns napi::Error if value doesn't fit in i64.
pub(crate) fn bigint_to_i64(value: BigInt, error_msg: impl Display) -> napi::Result<i64> {
Expand Down
Morty Proxy This is a proxified and sanitized view of the page, visit original site.