use std::{convert::Infallible, error::Error as ErrorTrait, fmt};
use crate::{auth::AuthenticationClientFeedback, ffi_sdk};
pub type Result<Ok, Err = DittoError> = ::core::result::Result<Ok, Err>;
pub struct DittoError {
repr: Repr,
}
enum Repr {
Simple(ErrorKind),
Authentication(AuthenticationClientFeedback),
Ffi(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, Ffi, Internal,
InvalidInput, IO,
License, NotActivated, NonExtant, ReleasedDittoInstance, }
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::Ffi => "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 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 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 fn license(err: LicenseError) -> DittoError {
DittoError {
repr: Repr::License(err),
}
}
pub fn from_ffi(kind: ErrorKind) -> DittoError {
match unsafe { ffi_sdk::ditto_error_message() } {
Some(c_msg) => {
let msg = c_msg.into_string();
DittoError::new(kind, msg)
}
None => DittoError {
repr: Repr::Ffi(FfiError {
msg: "No Message".to_owned(),
code: -1, }),
},
}
}
pub fn kind(&self) -> ErrorKind {
match &self.repr {
Repr::Simple(kind) => *kind,
Repr::Rust(e) => e.kind,
Repr::Ffi(_c) => ErrorKind::Ffi, 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::Ffi(ref c) => fmt
.debug_struct("FFIError")
.field("code", &c.code)
.field("message", &c.msg)
.field("kind", &ErrorKind::Ffi)
.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::Ffi(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 unsafe { 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::Ffi(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)
}
}