Skip to main content

mprobe_diagnostics/
error.rs

1//! Defines the `Error` types that this crate uses.
2
3use std::convert::From;
4use std::error::Error;
5use std::fmt;
6use std::fmt::Display;
7use std::fmt::Formatter;
8use std::io;
9use std::num::TryFromIntError;
10use std::sync::Arc;
11
12use bson::error::Error as BsonError;
13use bson::error::ErrorKind as BsonErrorKind;
14use bson::error::ValueAccessErrorKind;
15
16/// The error type for parsing diagnostic metrics.
17///
18/// Errors mostly originate from I/O read operations, BSON deserialization,
19/// and field value accesses.
20#[derive(Debug, Clone)]
21pub enum MetricParseError {
22    /// A [std::io::Error] encountered while reading diagnostic metrics.
23    Io(Arc<io::Error>),
24
25    /// A [bson::de::Error] encountered while deserializing BSON documents.
26    BsonDeserialzation(BsonError),
27
28    /// A [KeyAccessError] encountered while accessing BSON fields.
29    FieldAccess(KeyAccessError),
30
31    /// Unknown BSON document read from the diagnostic metrics.
32    UnknownDocumentKind(i32),
33
34    /// The amount of metrics in the reference document is different
35    /// than the amount of metric samples.
36    MetricCountMismatch,
37
38    /// The timestamp for the given metric is missing.
39    MetricTimestampNotFound {
40        /// Metric name
41        name: Arc<str>,
42    },
43
44    /// A [TryFromIntError] encountered while converting integer values from [i32] to
45    /// [usize].
46    IntConversion(TryFromIntError),
47}
48
49impl Display for MetricParseError {
50    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
51        let parse_error = "metric parse error:";
52
53        match self {
54            MetricParseError::Io(error) => write!(f, "{parse_error} I/O error: {error}"),
55            MetricParseError::BsonDeserialzation(error) => {
56                write!(f, "{parse_error} BSON deserialization error: {error}")
57            }
58            MetricParseError::FieldAccess(error) => write!(
59                f,
60                "{parse_error} could not read the document field: {error}"
61            ),
62            MetricParseError::MetricCountMismatch => write!(
63                f,
64                "{parse_error} the amount of metrics in the reference document is different than the amount of metric samples"
65            ),
66            MetricParseError::MetricTimestampNotFound { name } => write!(
67                f,
68                "{parse_error} the metric timestamps for the {name} metric could not be found",
69            ),
70            MetricParseError::IntConversion(error) => {
71                write!(f, "{parse_error} could not parse integer: {error}")
72            }
73            MetricParseError::UnknownDocumentKind(value) => {
74                write!(f, "{parse_error} unknonw document type: {value}")
75            }
76        }
77    }
78}
79
80impl Error for MetricParseError {
81    fn source(&self) -> Option<&(dyn Error + 'static)> {
82        match self {
83            MetricParseError::Io(error) => Some(error),
84            MetricParseError::BsonDeserialzation(error) => Some(error),
85            MetricParseError::FieldAccess(error) => Some(error),
86            MetricParseError::MetricCountMismatch => None,
87            MetricParseError::MetricTimestampNotFound { .. } => None,
88            MetricParseError::IntConversion(error) => Some(error),
89            MetricParseError::UnknownDocumentKind(_) => None,
90        }
91    }
92}
93
94impl From<io::Error> for MetricParseError {
95    fn from(error: io::Error) -> Self {
96        MetricParseError::Io(Arc::new(error))
97    }
98}
99
100impl From<BsonError> for MetricParseError {
101    fn from(error: BsonError) -> Self {
102        MetricParseError::BsonDeserialzation(error)
103    }
104}
105
106impl From<TryFromIntError> for MetricParseError {
107    fn from(error: TryFromIntError) -> Self {
108        MetricParseError::IntConversion(error)
109    }
110}
111
112impl From<KeyAccessError> for MetricParseError {
113    fn from(error: KeyAccessError) -> Self {
114        MetricParseError::FieldAccess(error)
115    }
116}
117
118/// The error type for accessing BSON fields.
119#[derive(Debug, Clone)]
120pub enum KeyAccessError {
121    /// Could not find the field with the specified key.
122    KeyNotFound {
123        /// Key field name.
124        key: String,
125    },
126
127    /// The field with the specified key was found, but not with the expected type.
128    UnexpectedKeyType {
129        /// Key field name.
130        key: String,
131    },
132
133    /// Could not access field value with the specified key due to an unknown error.
134    AccessError {
135        /// Key field name.
136        key: String,
137
138        /// Underlying error message.
139        error: String,
140    },
141}
142
143impl Display for KeyAccessError {
144    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
145        let key_access_error = "key access error:";
146        match *self {
147            KeyAccessError::KeyNotFound { ref key } => {
148                write!(
149                    f,
150                    "{key_access_error} could not find the field with the {key} key"
151                )
152            }
153            KeyAccessError::UnexpectedKeyType { ref key } => write!(
154                f,
155                "{key_access_error} the field with {key} key was found, but not with the expected type"
156            ),
157            KeyAccessError::AccessError { ref key, ref error } => {
158                write!(
159                    f,
160                    "{key_access_error} could not access the field value with the {key} key. {error}"
161                )
162            }
163        }
164    }
165}
166
167impl Error for KeyAccessError {}
168
169pub(crate) trait ValueAccessResultExt<T> {
170    fn map_value_access_err(self, key: &str) -> Result<T, KeyAccessError>;
171}
172
173impl<T> ValueAccessResultExt<T> for Result<T, BsonError> {
174    fn map_value_access_err(self, key: &str) -> Result<T, KeyAccessError> {
175        self.map_err(|error| match error.kind {
176            BsonErrorKind::ValueAccess { kind, .. } => match kind {
177                ValueAccessErrorKind::NotPresent { .. } => KeyAccessError::KeyNotFound {
178                    key: key.to_owned(),
179                },
180                ValueAccessErrorKind::UnexpectedType { .. } => KeyAccessError::UnexpectedKeyType {
181                    key: key.to_owned(),
182                },
183                e => KeyAccessError::AccessError {
184                    key: key.to_owned(),
185                    error: e.to_string(),
186                },
187            },
188            _ => KeyAccessError::AccessError {
189                key: key.to_owned(),
190                error: format!("{error}"),
191            },
192        })
193    }
194}