1use std::{convert::Infallible, error::Error as ErrorTrait, fmt};
8
9use crate::{
10 ffi_sdk::{self, ffi_utils::repr_c},
11 identity::AuthenticationClientFeedback,
12};
13
14pub type Result<Ok, Err = DittoError> = ::core::result::Result<Ok, Err>;
16
17#[doc(inline)]
20pub use ffi_sdk::FfiErrorCode as CoreApiErrorKind;
21
22pub struct DittoError {
36 repr: Repr,
37}
38
39pub(crate) struct FfiError {
40 pub(crate) code: ::ffi_sdk::FfiErrorCode,
41 raw: repr_c::Box<::ffi_sdk::FfiError>,
42}
43
44impl ::core::fmt::Debug for FfiError {
45 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
46 f.debug_struct("FfiError")
47 .field("code", &self.code)
48 .field(
49 "description",
50 &::ffi_sdk::dittoffi_error_description(&self.raw),
51 )
52 .finish_non_exhaustive()
53 }
54}
55
56impl ::core::fmt::Display for FfiError {
57 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
58 let Self { code, raw } = self;
59 write!(
60 f,
61 "{code:?}: {}",
62 ::ffi_sdk::dittoffi_error_description(raw)
63 )?;
64 Ok(())
65 }
66}
67
68impl ::std::error::Error for FfiError {}
69
70#[derive(Debug)]
71enum Repr {
72 Simple(ErrorKind),
73 Authentication(AuthenticationClientFeedback),
74 Ffi(FfiError),
75 FfiLegacy(legacy::FfiError),
76 Rust(RustError),
77 License(LicenseTokenError),
78}
79
80#[derive(Debug)]
81pub(crate) enum LicenseTokenError {
83 VerificationFailed { message: String },
84 Expired { message: String },
85 UnsupportedFutureVersion { message: String },
86}
87
88impl LicenseTokenError {
89 pub fn message(&self) -> &String {
91 match self {
92 LicenseTokenError::VerificationFailed { message } => message,
93 LicenseTokenError::Expired { message } => message,
94 LicenseTokenError::UnsupportedFutureVersion { message } => message,
95 }
96 }
97}
98
99#[derive(Clone, Copy, Debug, PartialEq)]
100#[non_exhaustive]
101pub enum ErrorKind {
103 Authentication,
105
106 Config, FfiLegacy, Internal,
114
115 InvalidInput, IO, License, NotActivated, NonExtant, ReleasedDittoInstance,
133
134 CoreApi(CoreApiErrorKind),
136
137 Deprecation,
139}
140
141mod legacy {
144 #[derive(Debug)]
145 pub(crate) struct FfiError {
149 pub(crate) code: i32,
150 pub(crate) msg: String,
151 }
152}
153
154pub(crate) struct RustError {
156 pub(crate) kind: ErrorKind,
157 pub(crate) error: Box<dyn ErrorTrait + Send + Sync>,
158}
159
160impl fmt::Debug for RustError {
161 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
162 let msg = format!("{:?} - {}", &self.kind, self.kind);
164 fmt.debug_struct("RustError")
165 .field("kind", &msg)
166 .field("error", &self.error) .finish()
168 }
169}
170
171impl ErrorKind {
172 fn as_str(&self) -> &'static str {
173 match *self {
174 ErrorKind::Authentication => "Unable to authenticate Ditto",
175 ErrorKind::Config => "Required configuration values are missing or invalid",
176 ErrorKind::FfiLegacy => "Unmapped Ditto Error",
177 ErrorKind::IO => "There is a problem with the underlying file, directory, or network socket",
178 ErrorKind::Internal => "Ditto encountered an internal error",
179 ErrorKind::InvalidInput => "Invalid client input provided",
180 ErrorKind::License => "License token error",
181 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.",
182 ErrorKind::NonExtant => "The target entity can no longer be found",
183 ErrorKind::ReleasedDittoInstance => "The related Ditto instance has been closed",
184 ErrorKind::CoreApi(_) => "\
185 an error occurred from core Ditto functionality. \
186 Please use the `Display` implementation for more info.\
187 ",
188 ErrorKind::Deprecation => "Deprecated method called",
189 }
190 }
191}
192
193impl ::core::fmt::Display for ErrorKind {
194 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
195 match self {
196 ErrorKind::CoreApi(ffi_error_code) => write!(f, "{ffi_error_code:?}"),
197 _ => self.as_str().fmt(f),
198 }
199 }
200}
201
202impl From<ErrorKind> for DittoError {
203 fn from(kind: ErrorKind) -> DittoError {
204 DittoError {
205 repr: Repr::Simple(kind),
206 }
207 }
208}
209
210impl fmt::Debug for DittoError {
211 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212 fmt::Debug::fmt(&self.repr, f)
213 }
214}
215
216impl ErrorTrait for DittoError {}
217
218impl DittoError {
219 pub(crate) fn new<E>(kind: ErrorKind, rust_err: E) -> Self
221 where
222 E: Into<Box<dyn ErrorTrait + Send + Sync>>,
223 {
224 DittoError {
225 repr: Repr::Rust(RustError {
226 kind,
227 error: rust_err.into(),
228 }),
229 }
230 }
231}
232
233impl DittoError {
234 pub(crate) fn from_str(kind: ErrorKind, msg: impl Into<String>) -> DittoError {
236 let msg: String = msg.into();
237 DittoError {
238 repr: Repr::Rust(RustError {
239 kind,
240 error: msg.into(),
241 }),
242 }
243 }
244
245 pub(crate) fn from_small_peer_info_error_code(error_code: i32) -> DittoError {
247 match error_code {
248 -1 => Self::from_str(
249 ErrorKind::InvalidInput,
250 "The observability subsystem is unavailable",
251 ),
252 1 => Self::from_str(
253 ErrorKind::InvalidInput,
254 "The amount of data is too large according to our self-imposed limits.",
255 ),
256 2 => Self::from_str(
257 ErrorKind::InvalidInput,
258 "The amount of JSON data is too nested acccording to our self-imposed limits, or \
259 if the data cannot be parsed to determine the depth.",
260 ),
261 3 => Self::from_str(
262 ErrorKind::InvalidInput,
263 "The data cannot be parsed as a Map<String, Value>.",
264 ),
265 _ => Self::from_str(ErrorKind::FfiLegacy, "Unmapped error"),
266 }
267 }
268
269 pub(crate) fn license(err: LicenseTokenError) -> Self {
271 DittoError {
272 repr: Repr::License(err),
273 }
274 }
275
276 pub(crate) fn from_ffi(kind: ErrorKind) -> Self {
280 let msg = match ffi_sdk::ditto_error_message() {
281 Some(c_msg) => c_msg.into_string(),
282 None => "no message".into(),
283 };
284 DittoError::new(kind, msg)
285 }
286
287 pub fn kind(&self) -> ErrorKind {
289 match &self.repr {
290 Repr::Simple(kind) => *kind,
291 Repr::Rust(e) => e.kind,
292 Repr::FfiLegacy(_c) => ErrorKind::FfiLegacy, Repr::License(_) => ErrorKind::License,
295 Repr::Authentication(_) => ErrorKind::Authentication,
296 Repr::Ffi(ffi) => ErrorKind::CoreApi(ffi.code),
297 }
298 }
299
300 pub fn get_authentication_client_feedback(&self) -> Option<AuthenticationClientFeedback> {
305 if let Repr::Authentication(ref feedback) = self.repr {
306 Some(feedback.clone())
307 } else {
308 None
309 }
310 }
311
312 pub(crate) fn from_authentication_feedback(feedback: AuthenticationClientFeedback) -> Self {
314 DittoError {
315 repr: Repr::Authentication(feedback),
316 }
317 }
318}
319
320impl fmt::Display for DittoError {
321 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
322 match self.repr {
323 Repr::Simple(kind) => write!(fmt, "{}", kind),
324 Repr::Rust(ref e) => e.error.fmt(fmt), Repr::FfiLegacy(ref c) => write!(fmt, "{} (code {})", c.msg, c.code),
326 Repr::License(ref e) => write!(fmt, "{}", e.message()),
327 Repr::Authentication(ref feedback) => match feedback.feedback {
328 Some(ref feedback) => {
329 write!(fmt, "Authentication Error with feedback: {}", feedback)
330 }
331 None => {
332 write!(fmt, "Authentication Error")
333 }
334 },
335 Repr::Ffi(ref ffi_error) => ffi_error.fmt(fmt),
336 }
337 }
338}
339
340error_from_i32! {
341 i32, ::core::num::NonZeroI32
342}
343#[rustfmt::skip]
344macro_rules! error_from_i32 {(
345 $( $i32:ty ),* $(,)?
346) => (
347 $(
348 impl From<$i32> for legacy::FfiError {
349 fn from(code: $i32) -> legacy::FfiError {
350 let code: i32 = code.into();
351 debug_assert_ne!(code, 0);
352 match ffi_sdk::ditto_error_message() {
353 Some(c_msg) => {
354 let msg = c_msg.into_string();
355 legacy::FfiError { code, msg }
356 }
357 None => legacy::FfiError {
358 msg: "No Message".to_owned(),
359 code,
360 },
361 }
362 }
363 }
364
365 impl From<$i32> for DittoError {
366 fn from(code: $i32) -> Self {
367 DittoError { repr: Repr::FfiLegacy(code.into()) }
368 }
369 }
370 )*
371)}
372use error_from_i32;
373
374impl From<::serde_cbor::Error> for DittoError {
375 fn from(err: ::serde_cbor::Error) -> Self {
376 DittoError::new(ErrorKind::InvalidInput, err)
377 }
378}
379
380impl From<::serde_json::Error> for DittoError {
381 fn from(err: ::serde_json::Error) -> Self {
382 DittoError::new(ErrorKind::InvalidInput, err)
383 }
384}
385
386impl From<::std::io::Error> for DittoError {
387 fn from(err: ::std::io::Error) -> Self {
388 DittoError::new(ErrorKind::IO, err)
389 }
390}
391
392impl From<Infallible> for DittoError {
393 fn from(err: Infallible) -> Self {
394 DittoError::new(ErrorKind::Internal, err)
395 }
396}
397
398impl From<repr_c::Box_<ffi_sdk::FfiError>> for DittoError {
399 fn from(raw: repr_c::Box<ffi_sdk::FfiError>) -> Self {
400 DittoError {
401 repr: Repr::Ffi(FfiError {
402 code: ::ffi_sdk::dittoffi_error_code(&*raw),
403 raw,
404 }),
405 }
406 }
407}
408
409impl From<::tokio::task::JoinError> for DittoError {
410 fn from(err: ::tokio::task::JoinError) -> Self {
411 DittoError::new(ErrorKind::Internal, err)
412 }
413}