1use std::borrow::Cow;
12use std::num::{ParseIntError, TryFromIntError};
13
14pub use error_support::{debug, error, info, trace, warn};
16#[cfg(feature = "stateful")]
17use firefox_versioning::error::VersionParsingError;
18
19#[derive(Debug, thiserror::Error)]
20pub enum NimbusError {
21 #[error("Initialization of the database is not yet complete")]
22 DatabaseNotReady,
23
24 #[error("Empty ratios!")]
25 EmptyRatiosError,
26
27 #[error("EvaluationError: {0}")]
28 EvaluationError(String),
29
30 #[error("IO error: {0}")]
31 IOError(#[from] std::io::Error),
32
33 #[error("Internal error: {0}")]
34 InternalError(&'static str),
35
36 #[error("Invalid experiment data received")]
37 InvalidExperimentFormat,
38
39 #[error("Invalid Expression - didn't evaluate to a bool")]
40 InvalidExpression,
41
42 #[error("InvalidFractionError: Should be between 0 and 1")]
43 InvalidFraction,
44
45 #[error("Invalid path: {0}")]
46 InvalidPath(String),
47
48 #[error("Invalid persisted data")]
49 InvalidPersistedData,
50
51 #[error("JSON Error: {0} — {1}")]
52 JSONError(String, String),
53
54 #[error("The branch {0} does not exist for the experiment {1}")]
55 NoSuchBranch(String, String),
56
57 #[error("The experiment {0} does not exist")]
58 NoSuchExperiment(String),
59
60 #[error("Attempt to access an element that is out of bounds")]
61 OutOfBoundsError,
62
63 #[error("ParseIntError: {0}")]
64 ParseIntError(#[from] ParseIntError),
65
66 #[error("Transform parameter error: {0}")]
67 TransformParameterError(String),
68
69 #[error("TryFromIntError: {0}")]
70 TryFromIntError(#[from] TryFromIntError),
71
72 #[error("TryInto error: {0}")]
73 TryFromSliceError(#[from] std::array::TryFromSliceError),
74
75 #[error("UniFFI callback error: {0}")]
76 UniFFICallbackError(#[from] uniffi::UnexpectedUniFFICallbackError),
77
78 #[error("Error parsing URL: {0}")]
79 UrlParsingError(#[from] url::ParseError),
80
81 #[error("UUID parsing error: {0}")]
82 UuidError(#[from] uuid::Error),
83
84 #[error("Error parsing a string into a version {0}")]
85 VersionParsingError(String),
86
87 #[cfg(feature = "stateful")]
89 #[error("Behavior error: {0}")]
90 BehaviorError(#[from] BehaviorError),
91
92 #[cfg(feature = "stateful")]
93 #[error("Error with Remote Settings client: {0}")]
94 ClientError(#[from] remote_settings::RemoteSettingsError),
95
96 #[cfg(feature = "stateful")]
97 #[error("Regex error: {0}")]
98 RegexError(#[from] regex::Error),
99
100 #[cfg(feature = "stateful")]
101 #[error("Rkv error: {0}")]
102 RkvError(#[from] rkv::StoreError),
103
104 #[cfg(not(feature = "stateful"))]
106 #[error("Error in Cirrus: {0}")]
107 CirrusError(#[from] CirrusClientError),
108}
109
110#[cfg(feature = "stateful")]
111#[derive(Debug, thiserror::Error)]
112pub enum BehaviorError {
113 #[error(r#"EventQueryParseError: "{0}" is not a valid EventQuery"#)]
114 EventQueryParseError(String),
115
116 #[error("EventQueryTypeParseError: {0} is not a valid EventQueryType")]
117 EventQueryTypeParseError(String),
118
119 #[error("IntervalParseError: {0} is not a valid Interval")]
120 IntervalParseError(String),
121
122 #[error("Invalid duration: {0}")]
123 InvalidDuration(String),
124
125 #[error("Invalid state: {0}")]
126 InvalidState(String),
127
128 #[error("The event store is not available on the targeting attributes")]
129 MissingEventStore,
130
131 #[error("The recorded context is not available on the nimbus client")]
132 MissingRecordedContext,
133
134 #[error(r#"TypeError: "{0}" is not of type {1}"#)]
135 TypeError(String, String),
136}
137
138#[cfg(not(feature = "stateful"))]
139#[derive(Debug, thiserror::Error)]
140pub enum CirrusClientError {
141 #[error("Request missing parameter: {0}")]
142 RequestMissingParameter(String),
143}
144
145#[cfg(test)]
146impl From<serde_json::Error> for NimbusError {
147 fn from(error: serde_json::Error) -> Self {
148 NimbusError::JSONError("test".into(), error.to_string())
149 }
150}
151
152impl<'a> From<jexl_eval::error::EvaluationError<'a>> for NimbusError {
153 fn from(eval_error: jexl_eval::error::EvaluationError<'a>) -> Self {
154 NimbusError::EvaluationError(eval_error.to_string())
155 }
156}
157
158#[cfg(feature = "stateful")]
159impl From<VersionParsingError> for NimbusError {
160 fn from(eval_error: VersionParsingError) -> Self {
161 NimbusError::VersionParsingError(eval_error.to_string())
162 }
163}
164
165pub type Result<T, E = NimbusError> = std::result::Result<T, E>;
166
167pub trait ErrorCode: std::error::Error {
170 fn error_code(&self) -> Cow<'static, str>;
172}
173
174#[cfg(feature = "stateful")]
175impl ErrorCode for NimbusError {
176 fn error_code(&self) -> Cow<'static, str> {
177 match self {
178 Self::BehaviorError(e) => format!("BehaviorError({})", e.error_code()).into(),
179 Self::ClientError(..) => "ClientError".into(),
180 Self::DatabaseNotReady => "DatabaseNotReady".into(),
181 Self::EmptyRatiosError => "EmptyRatiosError".into(),
182 Self::EvaluationError(..) => "EvaluationError".into(),
183 Self::IOError(e) => format!("IOError({:?})", e.kind()).into(),
184 Self::InternalError(..) => "InternalError".into(),
185 Self::InvalidExperimentFormat => "InvalidExperimentFormat".into(),
186 Self::InvalidExpression => "InvalidExpression".into(),
187 Self::InvalidFraction => "InvalidFraction".into(),
188 Self::InvalidPath(..) => "InvalidPath".into(),
189 Self::InvalidPersistedData => "InvalidPersistedData".into(),
190 Self::JSONError(..) => "JSONError".into(),
191 Self::NoSuchBranch(..) => "NoSuchBranch".into(),
192 Self::NoSuchExperiment(..) => "NoSuchExperiment".into(),
193 Self::OutOfBoundsError => "OutOfBoundsError".into(),
194 Self::ParseIntError(..) => "ParseIntError".into(),
195 Self::RegexError(..) => "RegexError".into(),
196 Self::RkvError(e) => format!("RkvError({})", e.error_code()).into(),
197 Self::TransformParameterError(..) => "TransformParameterError".into(),
198 Self::TryFromIntError(..) => "TryFromIntError".into(),
199 Self::TryFromSliceError(..) => "TryFromSliceError".into(),
200 Self::UniFFICallbackError(..) => "UniFFICallbackError".into(),
201 Self::UrlParsingError(..) => "UrlParsingError".into(),
202 Self::UuidError(..) => "UuidError".into(),
203 Self::VersionParsingError(..) => "VersionParsingError".into(),
204 }
205 }
206}
207
208#[cfg(feature = "stateful")]
209impl ErrorCode for rkv::StoreError {
210 fn error_code(&self) -> Cow<'static, str> {
211 match self {
212 Self::ManagerPoisonError => "ManagerPoisonError".into(),
213 Self::DatabaseCorrupted => "DatabaseCorrupted".into(),
214 Self::KeyValuePairNotFound => "KeyValuePairNotFound".into(),
215 Self::KeyValuePairBadSize => "KeyValuePairBadSize".into(),
216 Self::FileInvalid => "FileInvalid".into(),
217 Self::MapFull => "MapFull".into(),
218 Self::DbsFull => "DbsFull".into(),
219 Self::ReadersFull => "ReadersFull".into(),
220 Self::IoError(e) => format!("IoError({:?})", e.kind()).into(),
221 Self::UnsuitableEnvironmentPath(..) => "UnsuitableEnvironmentPath".into(),
222 Self::DataError(..) => "DataError".into(),
223 Self::SafeModeError(..) => "SafeModeError".into(),
224 Self::ReadTransactionAlreadyExists(..) => "ReadTransactionAlreadyExists".into(),
225 Self::OpenAttemptedDuringTransaction(..) => "OpenAttemptedDuringTransaction".into(),
226 }
227 }
228}
229
230#[cfg(feature = "stateful")]
231impl ErrorCode for BehaviorError {
232 fn error_code(&self) -> Cow<'static, str> {
233 match self {
234 Self::EventQueryParseError(..) => "EventQueryParseError",
235 Self::EventQueryTypeParseError(..) => "EventQueryTypeParseError",
236 Self::IntervalParseError(..) => "IntervalParseError",
237 Self::InvalidDuration(..) => "InvalidDuration",
238 Self::InvalidState(..) => "InvalidState",
239 Self::MissingEventStore => "MissingEventStore",
240 Self::MissingRecordedContext => "MissingRecordedContext",
241 Self::TypeError(..) => "TypeError",
242 }
243 .into()
244 }
245}