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>;
#[doc(inline)]
pub use ::ffi_sdk::FfiErrorCode as CoreApiErrorKind;
pub struct DittoError {
repr: Repr,
}
pub(crate) struct FfiError {
pub(crate) code: ::ffi_sdk::FfiErrorCode,
raw: repr_c::Box<::ffi_sdk::FfiError>,
}
impl ::core::fmt::Debug for FfiError {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.debug_struct("FfiError")
.field("code", &self.code)
.finish_non_exhaustive()
}
}
impl ::core::fmt::Display for FfiError {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
let Self { code, raw } = self;
write!(
f,
"{code:?}: {}",
::ffi_sdk::dittoffi_error_description(raw)
)?;
Ok(())
}
}
impl ::std::error::Error for FfiError {}
#[derive(Debug)]
enum Repr {
Simple(ErrorKind),
Authentication(AuthenticationClientFeedback),
Ffi(FfiError),
FfiLegacy(legacy::FfiError),
Rust(RustError),
License(LicenseError),
}
#[derive(Debug)]
pub(crate) 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, PartialEq)]
#[non_exhaustive]
pub enum ErrorKind {
Authentication,
Config, FfiLegacy, Internal,
InvalidInput, IO, License, NotActivated, NonExtant, ReleasedDittoInstance,
CoreApi(CoreApiErrorKind),
}
mod legacy {
#[derive(Debug)]
pub(crate) struct FfiError {
pub(crate) code: i32,
pub(crate) msg: String,
}
}
pub(crate) struct RustError {
pub(crate) kind: ErrorKind,
pub(crate) 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);
fmt.debug_struct("RustError")
.field("kind", &msg)
.field("error", &self.error) .finish()
}
}
impl ErrorKind {
#[deprecated(note = "use the `Display` implementation instead")]
#[doc(hidden)]
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",
ErrorKind::CoreApi(_) => "\
an error occurred from core Ditto functionality. \
Please use the `Display` implementation for more info.\
",
}
}
}
impl ::core::fmt::Display for ErrorKind {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
match self {
ErrorKind::CoreApi(ffi_error_code) => write!(f, "{ffi_error_code:?}"),
_ => {
#[allow(deprecated)]
{
self.as_str()
}
}
.fmt(f),
}
}
}
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,
Repr::Ffi(ffi) => ErrorKind::CoreApi(ffi.code),
}
}
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::Display for DittoError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.repr {
Repr::Simple(kind) => write!(fmt, "{}", kind),
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")
}
},
Repr::Ffi(ref ffi_error) => ffi_error.fmt(fmt),
}
}
}
error_from_i32! {
i32, ::core::num::NonZeroI32
}
#[rustfmt::skip]
macro_rules! error_from_i32 {(
$( $i32:ty ),* $(,)?
) => (
$(
impl From<$i32> for legacy::FfiError {
fn from(code: $i32) -> legacy::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();
legacy::FfiError { code, msg }
}
None => legacy::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(raw: repr_c::Box<ffi_sdk::FfiError>) -> Self {
DittoError {
repr: Repr::Ffi(FfiError {
code: ::ffi_sdk::dittoffi_error_code(&*raw),
raw,
}),
}
}
}