Skip to main content

elfo_core/
errors.rs

1use std::{collections::BTreeMap, fmt::Debug};
2
3use derive_more::{Display, Error, IsVariant};
4
5// === StartError ===
6
7#[derive(Clone, Error)]
8#[non_exhaustive]
9pub struct StartError {
10    pub errors: Vec<StartGroupError>,
11}
12
13impl StartError {
14    pub(crate) fn single(group: String, reason: String) -> Self {
15        Self {
16            errors: vec![StartGroupError { group, reason }],
17        }
18    }
19
20    pub(crate) fn multiple(errors: Vec<StartGroupError>) -> Self {
21        Self { errors }
22    }
23}
24
25fn group_errors(errors: Vec<StartGroupError>) -> BTreeMap<String, Vec<String>> {
26    // `BTreeMap` is used purely to provide consistent group order in error
27    // messages.
28    let mut group_errors = BTreeMap::<String, Vec<String>>::new();
29    for error in errors {
30        group_errors
31            .entry(error.group)
32            .or_default()
33            .push(error.reason);
34    }
35    // Sort errors to provide consistent order.
36    for errors in group_errors.values_mut() {
37        errors.sort();
38    }
39    group_errors
40}
41
42impl Debug for StartError {
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        if f.alternate() {
45            let mut s = f.debug_struct("StartError");
46            s.field("errors", &self.errors);
47            s.finish()
48        } else {
49            write!(f, "failed to start\n\n")?;
50            for (group, errors) in group_errors(self.errors.clone()) {
51                writeln!(f, "{group}:")?;
52                for error in errors {
53                    writeln!(f, "- {error}")?;
54                }
55                writeln!(f)?;
56            }
57            Ok(())
58        }
59    }
60}
61
62impl std::fmt::Display for StartError {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        write!(f, "failed to start: ")?;
65        let mut i = 1;
66        for (group, errors) in group_errors(self.errors.clone()) {
67            for error in errors {
68                write!(f, "{i}. {error} ({group}); ")?;
69                i += 1;
70            }
71        }
72        Ok(())
73    }
74}
75
76#[derive(Clone, Debug, Display, Error)]
77#[non_exhaustive]
78#[display("error from group {group}: {reason}")]
79pub struct StartGroupError {
80    pub group: String,
81    pub reason: String,
82}
83
84// === SendError ===
85
86#[derive(Clone, Debug, Display, Error)]
87#[display("mailbox closed")]
88pub struct SendError<T>(#[error(not(source))] pub T);
89
90impl<T> SendError<T> {
91    #[inline]
92    pub fn into_inner(self) -> T {
93        self.0
94    }
95
96    /// Transforms the inner message.
97    #[inline]
98    pub fn map<U>(self, f: impl FnOnce(T) -> U) -> SendError<U> {
99        SendError(f(self.0))
100    }
101}
102
103// === TrySendError ===
104
105#[derive(Clone, Debug, Display, Error)]
106pub enum TrySendError<T> {
107    /// The mailbox is full.
108    #[display("mailbox full")]
109    Full(#[error(not(source))] T),
110    /// The mailbox has been closed.
111    #[display("mailbox closed")]
112    Closed(#[error(not(source))] T),
113}
114
115impl<T> TrySendError<T> {
116    /// Converts the error into its inner value.
117    #[inline]
118    pub fn into_inner(self) -> T {
119        match self {
120            Self::Closed(inner) => inner,
121            Self::Full(inner) => inner,
122        }
123    }
124
125    /// Transforms the inner message.
126    #[inline]
127    pub fn map<U>(self, f: impl FnOnce(T) -> U) -> TrySendError<U> {
128        match self {
129            Self::Full(inner) => TrySendError::Full(f(inner)),
130            Self::Closed(inner) => TrySendError::Closed(f(inner)),
131        }
132    }
133
134    /// Returns whether the error is the `Full` variant.
135    #[inline]
136    pub fn is_full(&self) -> bool {
137        matches!(self, Self::Full(_))
138    }
139
140    /// Returns whether the error is the `Closed` variant.
141    #[inline]
142    pub fn is_closed(&self) -> bool {
143        matches!(self, Self::Closed(_))
144    }
145}
146
147impl<T> From<SendError<T>> for TrySendError<T> {
148    #[inline]
149    fn from(err: SendError<T>) -> Self {
150        TrySendError::Closed(err.0)
151    }
152}
153
154// === RequestError ===
155
156#[derive(Debug, Clone, IsVariant, Display, Error)]
157pub enum RequestError {
158    /// Receiver hasn't got the request.
159    #[display("request failed")]
160    Failed,
161    /// Receiver has got the request, but ignored it.
162    #[display("request ignored")]
163    Ignored,
164}
165
166// === TryRecvError ===
167
168#[derive(Debug, Clone, Display, Error)]
169pub enum TryRecvError {
170    /// The mailbox is empty.
171    #[display("mailbox empty")]
172    Empty,
173    /// The mailbox has been closed.
174    #[display("mailbox closed")]
175    Closed,
176}
177
178impl TryRecvError {
179    /// Returns whether the error is the `Empty` variant.
180    #[inline]
181    pub fn is_empty(&self) -> bool {
182        matches!(self, Self::Empty)
183    }
184
185    /// Returns whether the error is the `Closed` variant.
186    #[inline]
187    pub fn is_closed(&self) -> bool {
188        matches!(self, Self::Closed)
189    }
190}