Skip to main content

tigerbeetle_unofficial_core/
error.rs

1use std::{
2    error::Error,
3    fmt, mem,
4    num::{NonZeroU32, NonZeroU8},
5};
6
7pub use sys::{
8    generated_safe::{
9        self as sys_safe, CreateAccountErrorKind, CreateTransferErrorKind,
10        InitStatusErrorKind as NewClientErrorKind, PacketStatusErrorKind as SendErrorKind,
11    },
12    tb_create_accounts_result_t as RawCreateAccountsIndividualApiResult,
13    tb_create_transfers_result_t as RawCreateTransfersIndividualApiResult,
14};
15
16#[derive(Clone, Copy)]
17pub struct NewClientError(pub(crate) NonZeroU32);
18
19impl NewClientError {
20    const CODE_RANGE: std::ops::RangeInclusive<u32> =
21        sys_safe::MIN_INIT_STATUS_ERROR_CODE..=sys_safe::MAX_INIT_STATUS_ERROR_CODE;
22
23    pub fn kind(self) -> NewClientErrorKind {
24        let code = self.0.get();
25        if Self::CODE_RANGE.contains(&code) {
26            // SAFETY: We checked if it's in range right above.
27            unsafe { mem::transmute::<u32, NewClientErrorKind>(code) }
28        } else {
29            NewClientErrorKind::UnstableUncategorized
30        }
31    }
32
33    pub fn code(self) -> NonZeroU32 {
34        self.0
35    }
36}
37
38impl fmt::Debug for NewClientError {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        let mut d = f.debug_tuple("NewClientError");
41        let kind = self.kind();
42        if matches!(kind, NewClientErrorKind::UnstableUncategorized) {
43            let code = self.code();
44            d.field(&code);
45        } else {
46            d.field(&kind);
47        }
48        d.finish()
49    }
50}
51
52impl fmt::Display for NewClientError {
53    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54        use NewClientErrorKind as K;
55
56        match self.kind() {
57            K::AddressInvalid => "Replica addresses format is invalid",
58            K::AddressLimitExceeded => "Replica addresses limit exceeded",
59            K::NetworkSubsystem => "Internal client had unexpected networking issues",
60            K::OutOfMemory => "Internal client ran out of memory",
61            K::SystemResources => "Internal client ran out of system resources",
62            K::Unexpected => "Unexpected internal error",
63            _ => return write!(f, "Unknown error status: {}", self.code()),
64        }
65        .fmt(f)
66    }
67}
68
69impl Error for NewClientError {}
70
71impl From<NewClientErrorKind> for NewClientError {
72    /// Constructs a [`NewClientError`] out of the provided [`NewClientErrorKind`].
73    ///
74    /// # Panics
75    ///
76    /// Panics on the hidden [`NewClientErrorKind::UnstableUncategorized`] variant.
77    fn from(value: NewClientErrorKind) -> Self {
78        let this = Self(NonZeroU32::new(value as _).unwrap());
79        if matches!(this.kind(), NewClientErrorKind::UnstableUncategorized) {
80            panic!("NewClientErrorKind::{value:?}")
81        }
82        this
83    }
84}
85
86#[derive(Clone, Copy)]
87pub struct SendError(pub(crate) NonZeroU8);
88
89impl SendError {
90    const CODE_RANGE: std::ops::RangeInclusive<u8> =
91        sys_safe::MIN_PACKET_STATUS_ERROR_CODE..=sys_safe::MAX_PACKET_STATUS_ERROR_CODE;
92
93    pub fn kind(self) -> SendErrorKind {
94        let code = self.0.get();
95        if Self::CODE_RANGE.contains(&code) {
96            // SAFETY: We checked if it's in range right above.
97            unsafe { mem::transmute::<u8, SendErrorKind>(code) }
98        } else {
99            SendErrorKind::UnstableUncategorized
100        }
101    }
102
103    pub fn code(self) -> NonZeroU8 {
104        self.0
105    }
106}
107
108impl fmt::Debug for SendError {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        let mut d = f.debug_tuple("SendError");
111        let kind = self.kind();
112        if matches!(kind, SendErrorKind::UnstableUncategorized) {
113            let code = self.code();
114            d.field(&code);
115        } else {
116            d.field(&kind);
117        }
118        d.finish()
119    }
120}
121
122impl fmt::Display for SendError {
123    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        use SendErrorKind as K;
125
126        match self.kind() {
127            K::TooMuchData => "Too much data provided on this batch",
128            K::InvalidOperation => "Invalid operation",
129            K::InvalidDataSize => "Invalid data size",
130            K::ClientEvicted => "Client was evicted",
131            K::ClientReleaseTooLow => "Client was evicted: release too old",
132            K::ClientReleaseTooHigh => "Client was evicted: release too new",
133            K::ClientShutdown => "Client was closed",
134            _ => return write!(f, "Unknown error status: {}", self.code()),
135        }
136        .fmt(f)
137    }
138}
139
140impl Error for SendError {}
141
142impl From<SendErrorKind> for SendError {
143    /// Constructs a [`SendError`] out of the provided [`SendErrorKind`].
144    ///
145    /// # Panics
146    ///
147    /// Panics on the hidden [`SendErrorKind::UnstableUncategorized`] variant.
148    fn from(value: SendErrorKind) -> Self {
149        let this = Self(NonZeroU8::new(value as _).unwrap());
150        if matches!(this.kind(), SendErrorKind::UnstableUncategorized) {
151            panic!("SendErrorKind::{value:?}")
152        }
153        this
154    }
155}
156
157#[derive(Clone, Copy)]
158pub struct CreateAccountError(pub(crate) NonZeroU32);
159
160impl CreateAccountError {
161    const CODE_RANGE: std::ops::RangeInclusive<u32> =
162        sys_safe::MIN_CREATE_ACCOUNT_ERROR_CODE..=sys_safe::MAX_CREATE_ACCOUNT_ERROR_CODE;
163
164    pub fn kind(self) -> CreateAccountErrorKind {
165        let code = self.0.get();
166        if Self::CODE_RANGE.contains(&code) {
167            // SAFETY: We checked if it's in range right above.
168            unsafe { mem::transmute::<u32, CreateAccountErrorKind>(code) }
169        } else {
170            CreateAccountErrorKind::UnstableUncategorized
171        }
172    }
173
174    pub fn code(self) -> NonZeroU32 {
175        self.0
176    }
177}
178
179impl fmt::Debug for CreateAccountError {
180    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181        let mut d = f.debug_tuple("CreateAccountError");
182        let kind = self.kind();
183        if matches!(kind, CreateAccountErrorKind::UnstableUncategorized) {
184            d.field(&self.0);
185        } else {
186            d.field(&kind);
187        }
188        d.finish()
189    }
190}
191
192impl fmt::Display for CreateAccountError {
193    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194        write!(f, "{:?}", self.kind())
195    }
196}
197
198impl Error for CreateAccountError {}
199
200impl From<CreateAccountErrorKind> for CreateAccountError {
201    /// Constructs a [`CreateAccountError`] out of the provided [`CreateAccountErrorKind`].
202    ///
203    /// # Panics
204    ///
205    /// Panics on the hidden [`CreateAccountErrorKind::UnstableUncategorized`] variant.
206    fn from(value: CreateAccountErrorKind) -> Self {
207        let this = Self(NonZeroU32::new(value as _).unwrap());
208        if matches!(this.kind(), CreateAccountErrorKind::UnstableUncategorized) {
209            panic!("CreateAccountErrorKind::{value:?}")
210        }
211        this
212    }
213}
214
215/// Type indicating individual API error for account creation.
216///
217/// Safe to `transpose` from [`RawCreateAccountsIndividualApiResult`]
218/// if [`Self::from_raw_result_unchecked`] would also be safe.
219// INVARIANT: `self.0.result` must not be zero.
220#[derive(Clone, Copy)]
221#[repr(transparent)]
222pub struct CreateAccountsIndividualApiError(RawCreateAccountsIndividualApiResult);
223
224impl CreateAccountsIndividualApiError {
225    /// Create error from raw result.
226    ///
227    /// # Errors
228    ///
229    /// Returns `None` if `raw.result` is zero.
230    pub fn from_raw_result(raw: RawCreateAccountsIndividualApiResult) -> Option<Self> {
231        (raw.result != 0).then_some(Self(raw))
232    }
233
234    /// Create error from raw result. Unchecked version of [`Self::from_raw_result`].
235    ///
236    /// # Safety
237    ///
238    /// This function is unsafe. `raw.result` must not be zero.
239    pub unsafe fn from_raw_result_unchecked(raw: RawCreateAccountsIndividualApiResult) -> Self {
240        Self(raw)
241    }
242
243    /// Create vec of errors from vec of raw results.
244    ///
245    /// Retains only elements `r` of vec `v` that satisfy `r.result != 0`.
246    pub fn vec_from_raw_results(mut v: Vec<RawCreateAccountsIndividualApiResult>) -> Vec<Self> {
247        v.retain(|r| r.result != 0);
248        unsafe { Self::vec_from_raw_results_unchecked(v) }
249    }
250
251    /// Create vec of errors from vec of raw results. Unchecked version of
252    /// [`Self::vec_from_raw_results`]
253    ///
254    /// # Safety
255    ///
256    /// This function is unsafe. Every element `r` of vec `v` must satisfy
257    /// `r.result != 0`.
258    pub unsafe fn vec_from_raw_results_unchecked(
259        v: Vec<RawCreateAccountsIndividualApiResult>,
260    ) -> Vec<Self> {
261        let mut v = mem::ManuallyDrop::new(v);
262        let len = v.len();
263        let cap = v.capacity();
264        let ptr = v.as_mut_ptr().cast::<CreateAccountsIndividualApiError>();
265        // SAFETY: this is fine because `Vec::from_raw_parts` has pretty loose
266        // safety requirements, and since `CreateAccountsIndividualApiError` is
267        // just a transparent wrapper of `RawCreateAccountsIndividualApiResult`
268        // this is safe.
269        Vec::from_raw_parts(ptr, len, cap)
270    }
271
272    /// Get index of the failed account.
273    pub fn index(&self) -> u32 {
274        self.0.index
275    }
276
277    /// Get error stripped of context, like index.
278    pub fn inner(&self) -> CreateAccountError {
279        CreateAccountError(
280            // SAFETY: type invariant
281            unsafe { NonZeroU32::new_unchecked(self.0.result) },
282        )
283    }
284
285    /// Get kind of error to match upon.
286    pub fn kind(&self) -> CreateAccountErrorKind {
287        self.inner().kind()
288    }
289}
290
291impl fmt::Debug for CreateAccountsIndividualApiError {
292    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
293        f.debug_struct("CreateAccountsIndividualApiError")
294            .field("index", &self.index())
295            .field("inner", &self.inner())
296            .finish()
297    }
298}
299
300impl fmt::Display for CreateAccountsIndividualApiError {
301    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
302        write!(
303            f,
304            "`{}` error occurred at account with index {}",
305            self.inner(),
306            self.index(),
307        )
308    }
309}
310
311impl Error for CreateAccountsIndividualApiError {}
312
313// INVARIANT: `self.0` must not be empty.
314#[derive(Debug)]
315pub struct CreateAccountsApiError(Vec<CreateAccountsIndividualApiError>);
316
317impl CreateAccountsApiError {
318    /// Get a slice of individual errors. Never empty.
319    pub fn as_slice(&self) -> &[CreateAccountsIndividualApiError] {
320        &self.0
321    }
322
323    /// Create error from vec of raw results.
324    ///
325    /// # Errors
326    ///
327    /// Returns `None` if `v.is_empty()`.
328    pub fn from_errors(v: Vec<CreateAccountsIndividualApiError>) -> Option<Self> {
329        (!v.is_empty()).then_some(CreateAccountsApiError(v))
330    }
331
332    /// Create error from vec of raw results.
333    ///
334    /// Retains only results with errors.
335    pub fn from_raw_results(v: Vec<RawCreateAccountsIndividualApiResult>) -> Option<Self> {
336        Self::from_errors(CreateAccountsIndividualApiError::vec_from_raw_results(v))
337    }
338}
339
340impl AsRef<[CreateAccountsIndividualApiError]> for CreateAccountsApiError {
341    fn as_ref(&self) -> &[CreateAccountsIndividualApiError] {
342        &self.0
343    }
344}
345
346impl fmt::Display for CreateAccountsApiError {
347    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
348        write!(
349            f,
350            "{} api errors occurred at accounts' creation",
351            self.0.len(),
352        )
353    }
354}
355
356impl Error for CreateAccountsApiError {
357    fn source(&self) -> Option<&(dyn Error + 'static)> {
358        self.0.first().map(|e| e as _)
359    }
360}
361
362impl From<CreateAccountsIndividualApiError> for CreateAccountsApiError {
363    fn from(value: CreateAccountsIndividualApiError) -> Self {
364        CreateAccountsApiError(vec![value])
365    }
366}
367
368#[derive(Debug)]
369#[non_exhaustive]
370pub enum CreateAccountsError {
371    Send(SendError),
372    Api(CreateAccountsApiError),
373}
374
375impl Error for CreateAccountsError {
376    fn source(&self) -> Option<&(dyn Error + 'static)> {
377        Some(match self {
378            Self::Send(e) => e as _,
379            Self::Api(e) => e as _,
380        })
381    }
382}
383
384impl fmt::Display for CreateAccountsError {
385    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
386        write!(f, "Failed to create accounts: ")?;
387        match self {
388            Self::Send(e) => write!(f, "{e}"),
389            Self::Api(e) => write!(f, "{e}"),
390        }
391    }
392}
393
394impl From<SendError> for CreateAccountsError {
395    fn from(value: SendError) -> Self {
396        Self::Send(value)
397    }
398}
399
400impl From<CreateAccountsApiError> for CreateAccountsError {
401    fn from(value: CreateAccountsApiError) -> Self {
402        Self::Api(value)
403    }
404}
405
406#[derive(Clone, Copy)]
407pub struct CreateTransferError(pub(crate) NonZeroU32);
408
409impl CreateTransferError {
410    const CODE_RANGE: std::ops::RangeInclusive<u32> =
411        sys_safe::MIN_CREATE_TRANSFER_ERROR_CODE..=sys_safe::MAX_CREATE_TRANSFER_ERROR_CODE;
412
413    pub fn kind(self) -> CreateTransferErrorKind {
414        let code = self.0.get();
415        if Self::CODE_RANGE.contains(&code)
416            && !sys_safe::EXCLUDED_CREATE_TRANSFER_ERROR_CODES.contains(&code)
417        {
418            // SAFETY: We checked if it's in range right above.
419            unsafe { mem::transmute::<u32, CreateTransferErrorKind>(code) }
420        } else {
421            CreateTransferErrorKind::UnstableUncategorized
422        }
423    }
424
425    pub fn code(self) -> NonZeroU32 {
426        self.0
427    }
428}
429
430impl fmt::Debug for CreateTransferError {
431    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
432        let mut d = f.debug_tuple("CreateTransferError");
433        let kind = self.kind();
434        if matches!(kind, CreateTransferErrorKind::UnstableUncategorized) {
435            let code = self.code().get();
436            d.field(&code);
437        } else {
438            d.field(&kind);
439        }
440        d.finish()
441    }
442}
443
444impl fmt::Display for CreateTransferError {
445    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
446        write!(f, "{:?}", self.kind())
447    }
448}
449
450impl Error for CreateTransferError {}
451
452impl From<CreateTransferErrorKind> for CreateTransferError {
453    /// Constructs a [`CreateTransferError`] out of the provided [`CreateTransferErrorKind`].
454    ///
455    /// # Panics
456    ///
457    /// Panics on the hidden [`CreateTransferErrorKind::UnstableUncategorized`] variant.
458    fn from(value: CreateTransferErrorKind) -> Self {
459        let this = Self(NonZeroU32::new(value as _).unwrap());
460        if matches!(this.kind(), CreateTransferErrorKind::UnstableUncategorized) {
461            panic!("CreateTransferErrorKind::{value:?}")
462        }
463        this
464    }
465}
466
467/// Type indicating individual API error for transfer creation.
468///
469/// Safe to `transpose` from [`RawCreateTransfersIndividualApiResult`]
470/// if [`Self::from_raw_result_unchecked`] would also be safe.
471// INVARIANT: `self.0.result` must not be zero.
472#[derive(Clone, Copy)]
473#[repr(transparent)]
474pub struct CreateTransfersIndividualApiError(RawCreateTransfersIndividualApiResult);
475
476impl CreateTransfersIndividualApiError {
477    /// Create error from raw struct.
478    ///
479    /// # Errors
480    ///
481    /// Returns `None` if `raw.result` is zero.
482    pub fn from_raw_result(raw: RawCreateTransfersIndividualApiResult) -> Option<Self> {
483        (raw.result != 0).then_some(Self(raw))
484    }
485
486    /// Create error from raw struct. Unchecked version of [`Self::from_raw_result`].
487    ///
488    /// # Safety
489    ///
490    /// This function is unsafe. `raw.result` must not be zero.
491    pub unsafe fn from_raw_result_unchecked(raw: RawCreateTransfersIndividualApiResult) -> Self {
492        Self(raw)
493    }
494
495    /// Create vec of errors from vec of raw results.
496    ///
497    /// Retains only elements `r` of vec `v` that satisfy `r.result != 0`.
498    pub fn vec_from_raw_results(mut v: Vec<RawCreateTransfersIndividualApiResult>) -> Vec<Self> {
499        v.retain(|r| r.result != 0);
500        unsafe { Self::vec_from_raw_results_unchecked(v) }
501    }
502
503    /// Create vec of errors from vec of raw results. Unchecked version of
504    /// [`Self::vec_from_raw_results`]
505    ///
506    /// # Safety
507    ///
508    /// This function is unsafe. Every element `r` of vec `v` must satisfy
509    /// `r.result != 0`.
510    pub unsafe fn vec_from_raw_results_unchecked(
511        v: Vec<RawCreateTransfersIndividualApiResult>,
512    ) -> Vec<Self> {
513        let mut v = mem::ManuallyDrop::new(v);
514        let len = v.len();
515        let cap = v.capacity();
516        let ptr = v.as_mut_ptr().cast::<CreateTransfersIndividualApiError>();
517        // SAFETY: this is fine because `Vec::from_raw_parts` has pretty loose
518        // safety requirements, and since `CreateTransfersIndividualApiError` is
519        // just a transparent wrapper of `RawCreateTransfersIndividualApiResult`
520        // this is safe.
521        Vec::from_raw_parts(ptr, len, cap)
522    }
523
524    /// Get index of the failed transfer.
525    pub fn index(&self) -> u32 {
526        self.0.index
527    }
528
529    /// Get error stripped of context, like index.
530    pub fn inner(&self) -> CreateTransferError {
531        CreateTransferError(
532            // SAFETY: type invariant
533            unsafe { NonZeroU32::new_unchecked(self.0.result) },
534        )
535    }
536
537    /// Get kind of error to match upon.
538    pub fn kind(&self) -> CreateTransferErrorKind {
539        self.inner().kind()
540    }
541}
542
543impl fmt::Debug for CreateTransfersIndividualApiError {
544    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
545        f.debug_struct("CreateTransfersIndividualApiError")
546            .field("index", &self.index())
547            .field("inner", &self.inner())
548            .finish()
549    }
550}
551
552impl fmt::Display for CreateTransfersIndividualApiError {
553    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
554        write!(
555            f,
556            "`{}` error occurred at account with index {}",
557            self.inner(),
558            self.index(),
559        )
560    }
561}
562
563impl Error for CreateTransfersIndividualApiError {}
564
565// INVARIANT: `self.0` must not be empty.
566#[derive(Debug)]
567pub struct CreateTransfersApiError(Vec<CreateTransfersIndividualApiError>);
568
569impl CreateTransfersApiError {
570    /// Get a slice of individual errors. Never empty.
571    pub fn as_slice(&self) -> &[CreateTransfersIndividualApiError] {
572        &self.0
573    }
574
575    /// Create error from vec of raw results.
576    ///
577    /// # Errors
578    ///
579    /// Returns `None` if `v.is_empty()`.
580    pub fn from_errors(v: Vec<CreateTransfersIndividualApiError>) -> Option<Self> {
581        (!v.is_empty()).then_some(CreateTransfersApiError(v))
582    }
583
584    /// Create error from vec of raw results.
585    ///
586    /// Retains only results with errors.
587    pub fn from_raw_results(v: Vec<RawCreateTransfersIndividualApiResult>) -> Option<Self> {
588        Self::from_errors(CreateTransfersIndividualApiError::vec_from_raw_results(v))
589    }
590}
591
592impl AsRef<[CreateTransfersIndividualApiError]> for CreateTransfersApiError {
593    fn as_ref(&self) -> &[CreateTransfersIndividualApiError] {
594        &self.0
595    }
596}
597
598impl fmt::Display for CreateTransfersApiError {
599    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
600        write!(
601            f,
602            "{} api errors occurred at transfers' creation",
603            self.0.len(),
604        )
605    }
606}
607
608impl Error for CreateTransfersApiError {
609    fn source(&self) -> Option<&(dyn Error + 'static)> {
610        self.0.first().map(|e| e as _)
611    }
612}
613
614impl From<CreateTransfersIndividualApiError> for CreateTransfersApiError {
615    fn from(value: CreateTransfersIndividualApiError) -> Self {
616        Self(vec![value])
617    }
618}
619
620#[derive(Debug)]
621#[non_exhaustive]
622pub enum CreateTransfersError {
623    Send(SendError),
624    Api(CreateTransfersApiError),
625}
626
627impl Error for CreateTransfersError {
628    fn source(&self) -> Option<&(dyn Error + 'static)> {
629        Some(match self {
630            Self::Send(e) => e as _,
631            Self::Api(e) => e as _,
632        })
633    }
634}
635
636impl fmt::Display for CreateTransfersError {
637    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
638        write!(f, "Failed to create transfers: ")?;
639        match self {
640            Self::Send(e) => write!(f, "{e}"),
641            Self::Api(e) => write!(f, "{e}"),
642        }
643    }
644}
645
646impl From<SendError> for CreateTransfersError {
647    fn from(value: SendError) -> Self {
648        Self::Send(value)
649    }
650}
651
652impl From<CreateTransfersApiError> for CreateTransfersError {
653    fn from(value: CreateTransfersApiError) -> Self {
654        Self::Api(value)
655    }
656}