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 #[deprecated(note = "use the `Display` implementation instead")]
173 #[doc(hidden)]
174 pub fn as_str(&self) -> &'static str {
175 match *self {
176 ErrorKind::Authentication => "Unable to authenticate Ditto",
177 ErrorKind::Config => "Required configuration values are missing or invalid",
178 ErrorKind::FfiLegacy => "Unmapped Ditto Error",
179 ErrorKind::IO => "There is a problem with the underlying file, directory, or network socket",
180 ErrorKind::Internal => "Ditto encountered an internal error",
181 ErrorKind::InvalidInput => "Invalid client input provided",
182 ErrorKind::License => "License token error",
183 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.",
184 ErrorKind::NonExtant => "The target entity can no longer be found",
185 ErrorKind::ReleasedDittoInstance => "The related Ditto instance has been closed",
186 ErrorKind::CoreApi(_) => "\
187 an error occurred from core Ditto functionality. \
188 Please use the `Display` implementation for more info.\
189 ",
190 ErrorKind::Deprecation => "Deprecated method called",
191 }
192 }
193}
194
195impl ::core::fmt::Display for ErrorKind {
196 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
197 match self {
198 ErrorKind::CoreApi(ffi_error_code) => write!(f, "{ffi_error_code:?}"),
199 _ => {
200 #[allow(deprecated)]
201 {
202 self.as_str()
203 }
204 }
205 .fmt(f),
206 }
207 }
208}
209
210impl From<ErrorKind> for DittoError {
211 fn from(kind: ErrorKind) -> DittoError {
212 DittoError {
213 repr: Repr::Simple(kind),
214 }
215 }
216}
217
218impl fmt::Debug for DittoError {
219 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220 fmt::Debug::fmt(&self.repr, f)
221 }
222}
223
224impl ErrorTrait for DittoError {}
225
226impl DittoError {
227 pub(crate) fn new<E>(kind: ErrorKind, rust_err: E) -> Self
229 where
230 E: Into<Box<dyn ErrorTrait + Send + Sync>>,
231 {
232 DittoError {
233 repr: Repr::Rust(RustError {
234 kind,
235 error: rust_err.into(),
236 }),
237 }
238 }
239}
240
241impl DittoError {
242 pub(crate) fn from_str(kind: ErrorKind, msg: impl Into<String>) -> DittoError {
244 let msg: String = msg.into();
245 DittoError {
246 repr: Repr::Rust(RustError {
247 kind,
248 error: msg.into(),
249 }),
250 }
251 }
252
253 pub(crate) fn from_small_peer_info_error_code(error_code: i32) -> DittoError {
255 match error_code {
256 -1 => Self::from_str(
257 ErrorKind::InvalidInput,
258 "The observability subsystem is unavailable",
259 ),
260 1 => Self::from_str(
261 ErrorKind::InvalidInput,
262 "The amount of data is too large according to our self-imposed limits.",
263 ),
264 2 => Self::from_str(
265 ErrorKind::InvalidInput,
266 "The amount of JSON data is too nested acccording to our self-imposed limits, or \
267 if the data cannot be parsed to determine the depth.",
268 ),
269 3 => Self::from_str(
270 ErrorKind::InvalidInput,
271 "The data cannot be parsed as a Map<String, Value>.",
272 ),
273 _ => Self::from_str(ErrorKind::FfiLegacy, "Unmapped error"),
274 }
275 }
276
277 pub(crate) fn license(err: LicenseTokenError) -> Self {
279 DittoError {
280 repr: Repr::License(err),
281 }
282 }
283
284 pub(crate) fn from_ffi(kind: ErrorKind) -> Self {
288 let msg = match ffi_sdk::ditto_error_message() {
289 Some(c_msg) => c_msg.into_string(),
290 None => "no message".into(),
291 };
292 DittoError::new(kind, msg)
293 }
294
295 pub fn kind(&self) -> ErrorKind {
297 match &self.repr {
298 Repr::Simple(kind) => *kind,
299 Repr::Rust(e) => e.kind,
300 Repr::FfiLegacy(_c) => ErrorKind::FfiLegacy, Repr::License(_) => ErrorKind::License,
303 Repr::Authentication(_) => ErrorKind::Authentication,
304 Repr::Ffi(ffi) => ErrorKind::CoreApi(ffi.code),
305 }
306 }
307
308 pub fn get_authentication_client_feedback(&self) -> Option<AuthenticationClientFeedback> {
313 if let Repr::Authentication(ref feedback) = self.repr {
314 Some(feedback.clone())
315 } else {
316 None
317 }
318 }
319
320 pub(crate) fn from_authentication_feedback(feedback: AuthenticationClientFeedback) -> Self {
322 DittoError {
323 repr: Repr::Authentication(feedback),
324 }
325 }
326}
327
328impl fmt::Display for DittoError {
329 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
330 match self.repr {
331 Repr::Simple(kind) => write!(fmt, "{}", kind),
332 Repr::Rust(ref e) => e.error.fmt(fmt), Repr::FfiLegacy(ref c) => write!(fmt, "{} (code {})", c.msg, c.code),
334 Repr::License(ref e) => write!(fmt, "{}", e.message()),
335 Repr::Authentication(ref feedback) => match feedback.feedback {
336 Some(ref feedback) => {
337 write!(fmt, "Authentication Error with feedback: {}", feedback)
338 }
339 None => {
340 write!(fmt, "Authentication Error")
341 }
342 },
343 Repr::Ffi(ref ffi_error) => ffi_error.fmt(fmt),
344 }
345 }
346}
347
348error_from_i32! {
349 i32, ::core::num::NonZeroI32
350}
351#[rustfmt::skip]
352macro_rules! error_from_i32 {(
353 $( $i32:ty ),* $(,)?
354) => (
355 $(
356 impl From<$i32> for legacy::FfiError {
357 fn from(code: $i32) -> legacy::FfiError {
358 let code: i32 = code.into();
359 debug_assert_ne!(code, 0);
360 match ffi_sdk::ditto_error_message() {
361 Some(c_msg) => {
362 let msg = c_msg.into_string();
363 legacy::FfiError { code, msg }
364 }
365 None => legacy::FfiError {
366 msg: "No Message".to_owned(),
367 code,
368 },
369 }
370 }
371 }
372
373 impl From<$i32> for DittoError {
374 fn from(code: $i32) -> Self {
375 DittoError { repr: Repr::FfiLegacy(code.into()) }
376 }
377 }
378 )*
379)}
380use error_from_i32;
381
382impl From<::serde_cbor::Error> for DittoError {
383 fn from(err: ::serde_cbor::Error) -> Self {
384 DittoError::new(ErrorKind::InvalidInput, err)
385 }
386}
387
388impl From<::serde_json::Error> for DittoError {
389 fn from(err: ::serde_json::Error) -> Self {
390 DittoError::new(ErrorKind::InvalidInput, err)
391 }
392}
393
394impl From<::std::io::Error> for DittoError {
395 fn from(err: ::std::io::Error) -> Self {
396 DittoError::new(ErrorKind::IO, err)
397 }
398}
399
400impl From<Infallible> for DittoError {
401 fn from(err: Infallible) -> Self {
402 DittoError::new(ErrorKind::Internal, err)
403 }
404}
405
406impl From<repr_c::Box_<ffi_sdk::FfiError>> for DittoError {
407 fn from(raw: repr_c::Box<ffi_sdk::FfiError>) -> Self {
408 DittoError {
409 repr: Repr::Ffi(FfiError {
410 code: ::ffi_sdk::dittoffi_error_code(&*raw),
411 raw,
412 }),
413 }
414 }
415}
416
417impl From<::tokio::task::JoinError> for DittoError {
418 fn from(err: ::tokio::task::JoinError) -> Self {
419 DittoError::new(ErrorKind::Internal, err)
420 }
421}