use crate::cassandra::consistency::Consistency;
use crate::cassandra::util::{Protected, ProtectedInner};
use crate::cassandra::value::ValueType;
use crate::cassandra::write_type::WriteType;
use crate::cassandra_sys::cass_error_desc;
use crate::cassandra_sys::cass_error_result_free;
use crate::cassandra_sys::cass_false;
use crate::cassandra_sys::cass_future_error_message;
use crate::cassandra_sys::cass_future_get_error_result;
use crate::cassandra_sys::CassErrorResult as CassErrorResult_;
use crate::cassandra_sys::CassError_;
use crate::cassandra_sys::CassFuture as _Future;
use crate::cassandra_sys::CASS_OK;
use crate::cassandra_sys::{
cass_error_num_arg_types, cass_error_result_arg_type, cass_error_result_consistency,
cass_error_result_data_present, cass_error_result_function, cass_error_result_keyspace,
cass_error_result_num_failures, cass_error_result_responses_received,
cass_error_result_responses_required, cass_error_result_table, cass_error_result_write_type,
};
use crate::Session;
use std::ffi::CStr;
use std::fmt::Debug;
use std::{slice, str};
error_chain! {
foreign_links {
StringContainsNul(::std::ffi::NulError)
#[doc = "Attempted to pass a string containing `\\0` to Cassandra"];
InvalidUtf8(::std::str::Utf8Error)
#[doc = "Attempted to decode an invalid UTF-8-encoded string"];
}
errors {
CassError(code: CassErrorCode, msg: String) {
description("Cassandra error")
display("Cassandra error {:?}: {}", &code, &msg)
}
BatchSessionMismatch(batch_session: Session, statement_session: Session) {
description("Batch cannot add a statement belonging to another session.")
display("Batch's session {:?} != {:?}", &batch_session, &statement_session)
}
CassErrorResult(
code: CassErrorCode,
msg: String,
consistency: Consistency,
actual: i32,
required: i32,
num_failures: i32,
data_present: bool,
write_type: WriteType,
keyspace: Option<String>,
table: Option<String>,
function: Option<(String, Vec<String>)>
) {
description("Cassandra detailed error")
display("Cassandra detailed error {:?}: {}", &code, &msg)
}
UnsupportedType(expected: &'static str, actual: ValueType) {
description("Unsupported type")
display("Unsupported type {}; expected {}", actual, expected)
}
}
}
pub(crate) trait CassErrorExt {
fn to_result<T>(&self, default: T) -> Result<T>;
fn to_error(&self) -> Error;
}
impl CassErrorExt for CassError_ {
fn to_result<T>(&self, default: T) -> Result<T> {
unsafe {
match *self {
CASS_OK => Ok(default),
_ => {
let message = CStr::from_ptr(cass_error_desc(*self))
.to_string_lossy()
.into_owned();
Err(ErrorKind::CassError(CassErrorCode::build(*self), message).into())
}
}
}
}
fn to_error(&self) -> Error {
unsafe {
let message = CStr::from_ptr(cass_error_desc(*self))
.to_string_lossy()
.into_owned();
ErrorKind::CassError(CassErrorCode::build(*self), message).into()
}
}
}
impl CassErrorExt for CassErrorCode {
fn to_result<T>(&self, default: T) -> Result<T> {
self.inner().to_result(default)
}
fn to_error(&self) -> Error {
self.inner().to_error()
}
}
pub(crate) unsafe fn build_error_result(
code: CassErrorCode,
message: String,
e: *const CassErrorResult_,
) -> Error {
if e.is_null() {
ErrorKind::CassError(code, message).into()
} else {
let consistency = Consistency::build(cass_error_result_consistency(e));
let actual = cass_error_result_responses_received(e);
let required = cass_error_result_responses_required(e);
let num_failures = cass_error_result_num_failures(e);
let data_present = cass_error_result_data_present(e) != cass_false;
let write_type = WriteType::build(cass_error_result_write_type(e));
let keyspace = get_lossy_string(|s, s_len| cass_error_result_keyspace(e, s, s_len));
let table = get_lossy_string(|s, s_len| cass_error_result_table(e, s, s_len));
let function = get_lossy_string(|s, s_len| cass_error_result_function(e, s, s_len));
let function_call = function.map(|function| {
let i = cass_error_num_arg_types(e);
let mut args = vec![];
for i in 0..i {
let arg = get_lossy_string(|s, s_len| cass_error_result_arg_type(e, i, s, s_len))
.unwrap_or("<error>".to_string());
args.push(arg);
}
(function, args)
});
cass_error_result_free(e);
ErrorKind::CassErrorResult(
code,
message,
consistency,
actual,
required,
num_failures,
data_present,
write_type,
keyspace,
table,
function_call,
)
.into()
}
}
pub(crate) unsafe fn get_cass_future_error(rc: CassError_, inner: *mut _Future) -> Error {
let code = CassErrorCode::build(rc);
let message = get_lossy_string(|s, s_len| {
cass_future_error_message(inner, s, s_len);
CASS_OK
})
.unwrap(); build_error_result(code, message, cass_future_get_error_result(inner))
}
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
#[allow(missing_docs)] #[allow(non_camel_case_types)] #[non_exhaustive]
pub enum CassErrorCode {
LIB_BAD_PARAMS,
LIB_NO_STREAMS,
LIB_UNABLE_TO_INIT,
LIB_MESSAGE_ENCODE,
LIB_HOST_RESOLUTION,
LIB_UNEXPECTED_RESPONSE,
LIB_REQUEST_QUEUE_FULL,
LIB_NO_AVAILABLE_IO_THREAD,
LIB_WRITE_ERROR,
LIB_NO_HOSTS_AVAILABLE,
LIB_INDEX_OUT_OF_BOUNDS,
LIB_INVALID_ITEM_COUNT,
LIB_INVALID_VALUE_TYPE,
LIB_REQUEST_TIMED_OUT,
LIB_UNABLE_TO_SET_KEYSPACE,
LIB_CALLBACK_ALREADY_SET,
LIB_INVALID_STATEMENT_TYPE,
LIB_NAME_DOES_NOT_EXIST,
LIB_UNABLE_TO_DETERMINE_PROTOCOL,
LIB_NULL_VALUE,
LIB_NOT_IMPLEMENTED,
LIB_UNABLE_TO_CONNECT,
LIB_UNABLE_TO_CLOSE,
LIB_NO_PAGING_STATE,
LIB_PARAMETER_UNSET,
LIB_INVALID_ERROR_RESULT_TYPE,
LIB_INVALID_FUTURE_TYPE,
LIB_INTERNAL_ERROR,
LIB_INVALID_CUSTOM_TYPE,
LIB_INVALID_DATA,
LIB_NOT_ENOUGH_DATA,
LIB_INVALID_STATE,
LIB_NO_CUSTOM_PAYLOAD,
LIB_EXECUTION_PROFILE_INVALID,
LIB_NO_TRACING_ID,
SERVER_SERVER_ERROR,
SERVER_PROTOCOL_ERROR,
SERVER_BAD_CREDENTIALS,
SERVER_UNAVAILABLE,
SERVER_OVERLOADED,
SERVER_IS_BOOTSTRAPPING,
SERVER_TRUNCATE_ERROR,
SERVER_WRITE_TIMEOUT,
SERVER_READ_TIMEOUT,
SERVER_READ_FAILURE,
SERVER_FUNCTION_FAILURE,
SERVER_WRITE_FAILURE,
SERVER_SYNTAX_ERROR,
SERVER_UNAUTHORIZED,
SERVER_INVALID_QUERY,
SERVER_CONFIG_ERROR,
SERVER_ALREADY_EXISTS,
SERVER_UNPREPARED,
SSL_INVALID_CERT,
SSL_INVALID_PRIVATE_KEY,
SSL_NO_PEER_CERT,
SSL_INVALID_PEER_CERT,
SSL_IDENTITY_MISMATCH,
SSL_PROTOCOL_ERROR,
SSL_CLOSED,
}
enhance_nullary_enum!(CassErrorCode, CassError_, {
(LIB_BAD_PARAMS, CASS_ERROR_LIB_BAD_PARAMS, "LIB_BAD_PARAMS"),
(LIB_NO_STREAMS, CASS_ERROR_LIB_NO_STREAMS, "LIB_NO_STREAMS"),
(LIB_UNABLE_TO_INIT, CASS_ERROR_LIB_UNABLE_TO_INIT, "LIB_UNABLE_TO_INIT"),
(LIB_MESSAGE_ENCODE, CASS_ERROR_LIB_MESSAGE_ENCODE, "LIB_MESSAGE_ENCODE"),
(LIB_HOST_RESOLUTION, CASS_ERROR_LIB_HOST_RESOLUTION, "LIB_HOST_RESOLUTION"),
(LIB_UNEXPECTED_RESPONSE, CASS_ERROR_LIB_UNEXPECTED_RESPONSE, "LIB_UNEXPECTED_RESPONSE"),
(LIB_REQUEST_QUEUE_FULL, CASS_ERROR_LIB_REQUEST_QUEUE_FULL, "LIB_REQUEST_QUEUE_FULL"),
(LIB_NO_AVAILABLE_IO_THREAD, CASS_ERROR_LIB_NO_AVAILABLE_IO_THREAD, "LIB_NO_AVAILABLE_IO_THREAD"),
(LIB_WRITE_ERROR, CASS_ERROR_LIB_WRITE_ERROR, "LIB_WRITE_ERROR"),
(LIB_NO_HOSTS_AVAILABLE, CASS_ERROR_LIB_NO_HOSTS_AVAILABLE, "LIB_NO_HOSTS_AVAILABLE"),
(LIB_INDEX_OUT_OF_BOUNDS, CASS_ERROR_LIB_INDEX_OUT_OF_BOUNDS, "LIB_INDEX_OUT_OF_BOUNDS"),
(LIB_INVALID_ITEM_COUNT, CASS_ERROR_LIB_INVALID_ITEM_COUNT, "LIB_INVALID_ITEM_COUNT"),
(LIB_INVALID_VALUE_TYPE, CASS_ERROR_LIB_INVALID_VALUE_TYPE, "LIB_INVALID_VALUE_TYPE"),
(LIB_REQUEST_TIMED_OUT, CASS_ERROR_LIB_REQUEST_TIMED_OUT, "LIB_REQUEST_TIMED_OUT"),
(LIB_UNABLE_TO_SET_KEYSPACE, CASS_ERROR_LIB_UNABLE_TO_SET_KEYSPACE, "LIB_UNABLE_TO_SET_KEYSPACE"),
(LIB_CALLBACK_ALREADY_SET, CASS_ERROR_LIB_CALLBACK_ALREADY_SET, "LIB_CALLBACK_ALREADY_SET"),
(LIB_INVALID_STATEMENT_TYPE, CASS_ERROR_LIB_INVALID_STATEMENT_TYPE, "LIB_INVALID_STATEMENT_TYPE"),
(LIB_NAME_DOES_NOT_EXIST, CASS_ERROR_LIB_NAME_DOES_NOT_EXIST, "LIB_NAME_DOES_NOT_EXIST"),
(LIB_UNABLE_TO_DETERMINE_PROTOCOL, CASS_ERROR_LIB_UNABLE_TO_DETERMINE_PROTOCOL, "LIB_UNABLE_TO_DETERMINE_PROTOCOL"),
(LIB_NULL_VALUE, CASS_ERROR_LIB_NULL_VALUE, "LIB_NULL_VALUE"),
(LIB_NOT_IMPLEMENTED, CASS_ERROR_LIB_NOT_IMPLEMENTED, "LIB_NOT_IMPLEMENTED"),
(LIB_UNABLE_TO_CONNECT, CASS_ERROR_LIB_UNABLE_TO_CONNECT, "LIB_UNABLE_TO_CONNECT"),
(LIB_UNABLE_TO_CLOSE, CASS_ERROR_LIB_UNABLE_TO_CLOSE, "LIB_UNABLE_TO_CLOSE"),
(LIB_NO_PAGING_STATE, CASS_ERROR_LIB_NO_PAGING_STATE, "LIB_NO_PAGING_STATE"),
(LIB_PARAMETER_UNSET, CASS_ERROR_LIB_PARAMETER_UNSET, "LIB_PARAMETER_UNSET"),
(LIB_INVALID_ERROR_RESULT_TYPE, CASS_ERROR_LIB_INVALID_ERROR_RESULT_TYPE, "LIB_INVALID_ERROR_RESULT_TYPE"),
(LIB_INVALID_FUTURE_TYPE, CASS_ERROR_LIB_INVALID_FUTURE_TYPE, "LIB_INVALID_FUTURE_TYPE"),
(LIB_INTERNAL_ERROR, CASS_ERROR_LIB_INTERNAL_ERROR, "LIB_INTERNAL_ERROR"),
(LIB_INVALID_CUSTOM_TYPE, CASS_ERROR_LIB_INVALID_CUSTOM_TYPE, "LIB_INVALID_CUSTOM_TYPE"),
(LIB_INVALID_DATA, CASS_ERROR_LIB_INVALID_DATA, "LIB_INVALID_DATA"),
(LIB_NOT_ENOUGH_DATA, CASS_ERROR_LIB_NOT_ENOUGH_DATA, "LIB_NOT_ENOUGH_DATA"),
(LIB_INVALID_STATE, CASS_ERROR_LIB_INVALID_STATE, "LIB_INVALID_STATE"),
(LIB_NO_CUSTOM_PAYLOAD, CASS_ERROR_LIB_NO_CUSTOM_PAYLOAD, "LIB_NO_CUSTOM_PAYLOAD"),
(LIB_EXECUTION_PROFILE_INVALID, CASS_ERROR_LIB_EXECUTION_PROFILE_INVALID, "LIB_EXECUTION_PROFILE_INVALID"),
(LIB_NO_TRACING_ID, CASS_ERROR_LIB_NO_TRACING_ID, "LIB_NO_TRACING_ID"),
(SERVER_SERVER_ERROR, CASS_ERROR_SERVER_SERVER_ERROR, "SERVER_SERVER_ERROR"),
(SERVER_PROTOCOL_ERROR, CASS_ERROR_SERVER_PROTOCOL_ERROR, "SERVER_PROTOCOL_ERROR"),
(SERVER_BAD_CREDENTIALS, CASS_ERROR_SERVER_BAD_CREDENTIALS, "SERVER_BAD_CREDENTIALS"),
(SERVER_UNAVAILABLE, CASS_ERROR_SERVER_UNAVAILABLE, "SERVER_UNAVAILABLE"),
(SERVER_OVERLOADED, CASS_ERROR_SERVER_OVERLOADED, "SERVER_OVERLOADED"),
(SERVER_IS_BOOTSTRAPPING, CASS_ERROR_SERVER_IS_BOOTSTRAPPING, "SERVER_IS_BOOTSTRAPPING"),
(SERVER_TRUNCATE_ERROR, CASS_ERROR_SERVER_TRUNCATE_ERROR, "SERVER_TRUNCATE_ERROR"),
(SERVER_WRITE_TIMEOUT, CASS_ERROR_SERVER_WRITE_TIMEOUT, "SERVER_WRITE_TIMEOUT"),
(SERVER_READ_TIMEOUT, CASS_ERROR_SERVER_READ_TIMEOUT, "SERVER_READ_TIMEOUT"),
(SERVER_READ_FAILURE, CASS_ERROR_SERVER_READ_FAILURE, "SERVER_READ_FAILURE"),
(SERVER_FUNCTION_FAILURE, CASS_ERROR_SERVER_FUNCTION_FAILURE, "SERVER_FUNCTION_FAILURE"),
(SERVER_WRITE_FAILURE, CASS_ERROR_SERVER_WRITE_FAILURE, "SERVER_WRITE_FAILURE"),
(SERVER_SYNTAX_ERROR, CASS_ERROR_SERVER_SYNTAX_ERROR, "SERVER_SYNTAX_ERROR"),
(SERVER_UNAUTHORIZED, CASS_ERROR_SERVER_UNAUTHORIZED, "SERVER_UNAUTHORIZED"),
(SERVER_INVALID_QUERY, CASS_ERROR_SERVER_INVALID_QUERY, "SERVER_INVALID_QUERY"),
(SERVER_CONFIG_ERROR, CASS_ERROR_SERVER_CONFIG_ERROR, "SERVER_CONFIG_ERROR"),
(SERVER_ALREADY_EXISTS, CASS_ERROR_SERVER_ALREADY_EXISTS, "SERVER_ALREADY_EXISTS"),
(SERVER_UNPREPARED, CASS_ERROR_SERVER_UNPREPARED, "SERVER_UNPREPARED"),
(SSL_INVALID_CERT, CASS_ERROR_SSL_INVALID_CERT, "SSL_INVALID_CERT"),
(SSL_INVALID_PRIVATE_KEY, CASS_ERROR_SSL_INVALID_PRIVATE_KEY, "SSL_INVALID_PRIVATE_KEY"),
(SSL_NO_PEER_CERT, CASS_ERROR_SSL_NO_PEER_CERT, "SSL_NO_PEER_CERT"),
(SSL_INVALID_PEER_CERT, CASS_ERROR_SSL_INVALID_PEER_CERT, "SSL_INVALID_PEER_CERT"),
(SSL_IDENTITY_MISMATCH, CASS_ERROR_SSL_IDENTITY_MISMATCH, "SSL_IDENTITY_MISMATCH"),
(SSL_PROTOCOL_ERROR, CASS_ERROR_SSL_PROTOCOL_ERROR, "SSL_PROTOCOL_ERROR"),
(SSL_CLOSED, CASS_ERROR_SSL_CLOSED, "SSL_CLOSED"),
}, omit { CASS_OK, CASS_ERROR_LAST_ENTRY });
pub(crate) unsafe fn get_lossy_string<F>(get: F) -> Option<String>
where
F: Fn(*mut *const ::std::os::raw::c_char, *mut usize) -> CassError_,
{
let mut msg = std::ptr::null();
let mut msg_len = 0;
match (get)(&mut msg, &mut msg_len) {
CASS_OK => (),
_ => return None,
}
let slice = slice::from_raw_parts(msg as *const u8, msg_len);
Some(String::from_utf8_lossy(slice).into_owned())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
pub fn test_conversion() {
assert_eq!(
CassErrorCode::build(CassError_::CASS_ERROR_SERVER_PROTOCOL_ERROR),
CassErrorCode::SERVER_PROTOCOL_ERROR
);
match CassErrorCode::LIB_INVALID_DATA.inner() {
CassError_::CASS_ERROR_LIB_INVALID_DATA => (),
e => panic!("Unexpected return value {:?}", e),
}
}
#[test]
#[should_panic(expected = "Unexpected variant CassError_ :: CASS_OK")]
pub fn test_omitted_conversion_should_fail() {
CassErrorCode::build(CassError_::CASS_OK);
}
}