use std::{convert::Infallible, error::Error as ErrorTrait, fmt};
use crate::{
auth::authenticator::AuthenticationClientFeedback,
ffi_sdk::{self, ffi_utils::repr_c},
};
pub type Result<Ok, Err = DittoError> = ::core::result::Result<Ok, Err>;
pub struct DittoError {
repr: Repr,
}
enum Repr {
Simple(ErrorKind),
Authentication(AuthenticationClientFeedback),
FfiLegacy(legacy::FfiError),
Rust(RustError),
License(LicenseError),
}
pub enum LicenseError {
LicenseTokenVerificationFailed { message: String },
LicenseTokenExpired { message: String },
LicenseTokenUnsupportedFutureVersion { message: String },
}
impl LicenseError {
pub fn message(&self) -> &String {
match self {
LicenseError::LicenseTokenVerificationFailed { message } => message,
LicenseError::LicenseTokenExpired { message } => message,
LicenseError::LicenseTokenUnsupportedFutureVersion { message } => message,
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum ErrorKind {
Authentication,
Config, FfiLegacy, Internal,
InvalidInput, IO, License, NotActivated, NonExtant, ReleasedDittoInstance,
}
impl ErrorKind {
#[allow(nonstandard_style)]
#[deprecated]
pub const Ffi: Self = Self::FfiLegacy;
}
#[deprecated]
pub use legacy::FfiError;
mod legacy {
pub struct FfiError {
pub code: i32,
pub msg: String,
}
}
pub struct RustError {
pub kind: ErrorKind,
pub error: Box<dyn ErrorTrait + Send + Sync>,
}
impl fmt::Debug for RustError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let msg = format!("{:?} - {}", &self.kind, self.kind.as_str());
fmt.debug_struct("RustError")
.field("kind", &msg)
.field("error", &self.error) .finish()
}
}
impl ErrorKind {
pub fn as_str(&self) -> &'static str {
match *self {
ErrorKind::Authentication => "Unable to authenticate Ditto",
ErrorKind::Config => "Required configuration values are missing or invalid",
ErrorKind::FfiLegacy => "Unmapped Ditto Error",
ErrorKind::IO => "There is a problem with the underlying file, directory, or network socket",
ErrorKind::Internal => "Ditto encountered an internal error",
ErrorKind::InvalidInput => "Invalid client input provided",
ErrorKind::License => "License token error",
ErrorKind::NotActivated => "Sync could not be started because Ditto has not yet been activated. This can be achieved with a successful call to `set_license_token`. If you need to obtain a license token then please visit https://portal.ditto.live.",
ErrorKind::NonExtant => "The target entity can no longer be found",
ErrorKind::ReleasedDittoInstance => "The related Ditto instance has been closed",
}
}
}
impl From<ErrorKind> for DittoError {
fn from(kind: ErrorKind) -> DittoError {
DittoError {
repr: Repr::Simple(kind),
}
}
}
impl fmt::Debug for DittoError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.repr, f)
}
}
impl ErrorTrait for DittoError {}
impl DittoError {
pub(crate) fn new<E>(kind: ErrorKind, rust_err: E) -> DittoError
where
E: Into<Box<dyn ErrorTrait + Send + Sync>>,
{
DittoError {
repr: Repr::Rust(RustError {
kind,
error: rust_err.into(),
}),
}
}
pub(crate) fn from_str(kind: ErrorKind, msg: impl Into<String>) -> DittoError {
let msg: String = msg.into();
DittoError {
repr: Repr::Rust(RustError {
kind,
error: msg.into(),
}),
}
}
pub(crate) fn from_small_peer_info_error_code(error_code: i32) -> DittoError {
match error_code {
-1 => Self::from_str(
ErrorKind::InvalidInput,
"The observability subsystem is unavailable",
),
1 => Self::from_str(
ErrorKind::InvalidInput,
"The amount of data is too large according to our self-imposed limits.",
),
2 => Self::from_str(
ErrorKind::InvalidInput,
"The amount of JSON data is too nested acccording to our self-imposed limits, or \
if the data cannot be parsed to determine the depth.",
),
3 => Self::from_str(
ErrorKind::InvalidInput,
"The data cannot be parsed as a Map<String, Value>.",
),
_ => Self::from_str(ErrorKind::FfiLegacy, "Unmapped error"),
}
}
pub(crate) fn license(err: LicenseError) -> DittoError {
DittoError {
repr: Repr::License(err),
}
}
pub(crate) fn from_ffi(kind: ErrorKind) -> DittoError {
let msg = match ffi_sdk::ditto_error_message() {
Some(c_msg) => c_msg.into_string(),
None => "no message".into(),
};
DittoError::new(kind, msg)
}
pub fn kind(&self) -> ErrorKind {
match &self.repr {
Repr::Simple(kind) => *kind,
Repr::Rust(e) => e.kind,
Repr::FfiLegacy(_c) => ErrorKind::FfiLegacy, Repr::License(_) => ErrorKind::License,
Repr::Authentication(_) => ErrorKind::Authentication,
}
}
pub fn get_authentication_client_feedback(&self) -> Option<AuthenticationClientFeedback> {
if let Repr::Authentication(ref feedback) = self.repr {
Some(feedback.clone())
} else {
None
}
}
pub(crate) fn from_authentication_feedback(feedback: AuthenticationClientFeedback) -> Self {
DittoError {
repr: Repr::Authentication(feedback),
}
}
}
impl fmt::Debug for Repr {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Repr::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(),
Repr::Rust(ref e) => fmt::Debug::fmt(&e, fmt),
Repr::FfiLegacy(ref c) => fmt
.debug_struct("FFILegacyError")
.field("code", &c.code)
.field("message", &c.msg)
.field("kind", &ErrorKind::FfiLegacy)
.finish(),
Repr::License(ref e) => fmt
.debug_struct("LicenseTokenError")
.field("message", e.message())
.field("kind", &ErrorKind::License)
.finish(),
Repr::Authentication(ref feedback) => fmt
.debug_struct("AuthenticationError")
.field("Feedback", &feedback.feedback)
.finish(),
}
}
}
impl fmt::Display for DittoError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.repr {
Repr::Simple(kind) => write!(fmt, "{}", kind.as_str()),
Repr::Rust(ref e) => e.error.fmt(fmt), Repr::FfiLegacy(ref c) => write!(fmt, "{} (code {})", c.msg, c.code),
Repr::License(ref e) => write!(fmt, "{}", e.message()),
Repr::Authentication(ref feedback) => match feedback.feedback {
Some(ref feedback) => {
write!(fmt, "Authentication Error with feedback: {}", feedback)
}
None => {
write!(fmt, "Authentication Error")
}
},
}
}
}
error_from_i32! {
i32, ::core::num::NonZeroI32
}
#[rustfmt::skip]
macro_rules! error_from_i32 {(
$( $i32:ty ),* $(,)?
) => (
$(
impl From<$i32> for FfiError {
fn from(code: $i32) -> FfiError {
let code: i32 = code.into();
debug_assert_ne!(code, 0);
match ffi_sdk::ditto_error_message() {
Some(c_msg) => {
let msg = c_msg.into_string();
FfiError { code, msg }
}
None => FfiError {
msg: "No Message".to_owned(),
code,
},
}
}
}
impl From<$i32> for DittoError {
fn from(code: $i32) -> DittoError {
DittoError { repr: Repr::FfiLegacy(code.into()) }
}
}
)*
)}
use error_from_i32;
impl From<::serde_cbor::Error> for DittoError {
fn from(err: ::serde_cbor::Error) -> Self {
DittoError::new(ErrorKind::InvalidInput, err)
}
}
impl From<::serde_json::Error> for DittoError {
fn from(err: ::serde_json::Error) -> Self {
DittoError::new(ErrorKind::InvalidInput, err)
}
}
impl From<::std::io::Error> for DittoError {
fn from(err: ::std::io::Error) -> Self {
DittoError::new(ErrorKind::IO, err)
}
}
impl From<Infallible> for DittoError {
fn from(err: Infallible) -> Self {
DittoError::new(ErrorKind::Internal, err)
}
}
impl From<repr_c::Box_<ffi_sdk::FfiError>> for DittoError {
fn from(err: repr_c::Box<ffi_sdk::FfiError>) -> Self {
let err_code = ffi_sdk::dittoffi_error_code(&*err);
let raw_description = ffi_sdk::dittoffi_error_description(&*err);
DittoError {
repr: Repr::FfiLegacy(legacy::FfiError {
code: unsafe { ::core::mem::transmute::<_, i32>(err_code) },
msg: raw_description.to_string(),
}),
}
}
}