blob: 1a048b6b625257b89432dcdf4595b8cb1126d6b3 [file] [log] [blame]
Janis Danisevskis7d77a762020-07-20 13:03:31 -07001// Copyright 2020, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Keystore error provides convenience methods and types for Keystore error handling.
Janis Danisevskis7d77a762020-07-20 13:03:31 -070016//!
Tri Vocd6fc7a2023-08-31 11:46:32 -040017//! Here are some important types and helper functions:
Janis Danisevskis7d77a762020-07-20 13:03:31 -070018//!
Tri Vocd6fc7a2023-08-31 11:46:32 -040019//! `Error` type encapsulate Keystore, Keymint, and Binder errors. It is used internally by
20//! Keystore to diagnose error conditions that need to be reported to the client.
21//!
22//! `SerializedError` is used send error codes on the wire.
23//!
24//! `map_or_log_err` is a convenience method used to convert `anyhow::Error` into `SerializedError`
25//! wire type.
26//!
27//! Keystore functions should use `anyhow::Result` to return error conditions, and context should
28//! be added every time an error is forwarded.
Janis Danisevskis7d77a762020-07-20 13:03:31 -070029
Shawn Willden708744a2020-12-11 13:05:27 +000030pub use android_hardware_security_keymint::aidl::android::hardware::security::keymint::ErrorCode::ErrorCode;
Janis Danisevskisc5b210b2020-09-11 13:27:37 -070031pub use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
Janis Danisevskisc5b210b2020-09-11 13:27:37 -070032use android_system_keystore2::binder::{
Janis Danisevskisba998992020-12-29 16:08:40 -080033 ExceptionCode, Result as BinderResult, Status as BinderStatus, StatusCode,
Janis Danisevskis017d2092020-09-02 10:15:52 -070034};
Janis Danisevskis2ee014b2021-05-05 14:29:08 -070035use keystore2_selinux as selinux;
36use std::cmp::PartialEq;
Janis Danisevskisea03cff2021-12-16 08:10:17 -080037use std::ffi::CString;
Janis Danisevskis7d77a762020-07-20 13:03:31 -070038
39/// This is the main Keystore error type. It wraps the Keystore `ResponseCode` generated
40/// from AIDL in the `Rc` variant and Keymint `ErrorCode` in the Km variant.
Chris Wailes263de9f2022-08-11 15:00:51 -070041#[derive(Debug, thiserror::Error, PartialEq, Eq)]
Janis Danisevskis7d77a762020-07-20 13:03:31 -070042pub enum Error {
43 /// Wraps a Keystore `ResponseCode` as defined by the Keystore AIDL interface specification.
44 #[error("Error::Rc({0:?})")]
Janis Danisevskise24f3472020-08-12 17:58:49 -070045 Rc(ResponseCode),
Janis Danisevskis7d77a762020-07-20 13:03:31 -070046 /// Wraps a Keymint `ErrorCode` as defined by the Keymint AIDL interface specification.
47 #[error("Error::Km({0:?})")]
Janis Danisevskise24f3472020-08-12 17:58:49 -070048 Km(ErrorCode),
Janis Danisevskis017d2092020-09-02 10:15:52 -070049 /// Wraps a Binder exception code other than a service specific exception.
50 #[error("Binder exception code {0:?}, {1:?}")]
51 Binder(ExceptionCode, i32),
Janis Danisevskisba998992020-12-29 16:08:40 -080052 /// Wraps a Binder status code.
53 #[error("Binder transaction error {0:?}")]
54 BinderTransaction(StatusCode),
Janis Danisevskis7d77a762020-07-20 13:03:31 -070055}
56
57impl Error {
Janis Danisevskisc5b210b2020-09-11 13:27:37 -070058 /// Short hand for `Error::Rc(ResponseCode::SYSTEM_ERROR)`
Janis Danisevskis7d77a762020-07-20 13:03:31 -070059 pub fn sys() -> Self {
Janis Danisevskisc5b210b2020-09-11 13:27:37 -070060 Error::Rc(ResponseCode::SYSTEM_ERROR)
Janis Danisevskis7d77a762020-07-20 13:03:31 -070061 }
62
Seth Moore7ee79f92021-12-07 11:42:49 -080063 /// Short hand for `Error::Rc(ResponseCode::PERMISSION_DENIED)`
Janis Danisevskis7d77a762020-07-20 13:03:31 -070064 pub fn perm() -> Self {
Janis Danisevskisc5b210b2020-09-11 13:27:37 -070065 Error::Rc(ResponseCode::PERMISSION_DENIED)
Janis Danisevskis7d77a762020-07-20 13:03:31 -070066 }
67}
68
Janis Danisevskis017d2092020-09-02 10:15:52 -070069/// Helper function to map the binder status we get from calls into KeyMint
70/// to a Keystore Error. We don't create an anyhow error here to make
71/// it easier to evaluate KeyMint errors, which we must do in some cases, e.g.,
72/// when diagnosing authentication requirements, update requirements, and running
73/// out of operation slots.
74pub fn map_km_error<T>(r: BinderResult<T>) -> Result<T, Error> {
75 r.map_err(|s| {
76 match s.exception_code() {
77 ExceptionCode::SERVICE_SPECIFIC => {
78 let se = s.service_specific_error();
79 if se < 0 {
80 // Negative service specific errors are KM error codes.
Janis Danisevskisc5b210b2020-09-11 13:27:37 -070081 Error::Km(ErrorCode(s.service_specific_error()))
Janis Danisevskis017d2092020-09-02 10:15:52 -070082 } else {
83 // Non negative error codes cannot be KM error codes.
84 // So we create an `Error::Binder` variant to preserve
85 // the service specific error code for logging.
86 // `map_or_log_err` will map this on a system error,
87 // but not before logging the details to logcat.
88 Error::Binder(ExceptionCode::SERVICE_SPECIFIC, se)
89 }
90 }
91 // We create `Error::Binder` to preserve the exception code
92 // for logging.
93 // `map_or_log_err` will map this on a system error.
94 e_code => Error::Binder(e_code, 0),
95 }
96 })
97}
98
Janis Danisevskisba998992020-12-29 16:08:40 -080099/// This function is similar to map_km_error only that we don't expect
100/// any KeyMint error codes, we simply preserve the exception code and optional
101/// service specific exception.
102pub fn map_binder_status<T>(r: BinderResult<T>) -> Result<T, Error> {
103 r.map_err(|s| match s.exception_code() {
104 ExceptionCode::SERVICE_SPECIFIC => {
105 let se = s.service_specific_error();
106 Error::Binder(ExceptionCode::SERVICE_SPECIFIC, se)
107 }
108 ExceptionCode::TRANSACTION_FAILED => {
109 let e = s.transaction_error();
110 Error::BinderTransaction(e)
111 }
112 e_code => Error::Binder(e_code, 0),
113 })
114}
115
116/// This function maps a status code onto a Keystore Error.
117pub fn map_binder_status_code<T>(r: Result<T, StatusCode>) -> Result<T, Error> {
118 r.map_err(Error::BinderTransaction)
119}
120
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700121/// This function should be used by Keystore service calls to translate error conditions
Janis Danisevskis8ea5f552020-11-20 11:22:59 -0800122/// into service specific exceptions.
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700123///
Hasini Gunasinghee1d1bbd2021-04-20 18:13:25 +0000124/// All error conditions get logged by this function, except for KEY_NOT_FOUND error.
Janis Danisevskis8ea5f552020-11-20 11:22:59 -0800125///
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700126/// `handle_ok` will be called if `result` is `Ok(value)` where `value` will be passed
Janis Danisevskis8ea5f552020-11-20 11:22:59 -0800127/// as argument to `handle_ok`. `handle_ok` must generate a `BinderResult<T>`, but it
128/// typically returns Ok(value).
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700129///
130/// # Examples
131///
132/// ```
Janis Danisevskis8ea5f552020-11-20 11:22:59 -0800133/// fn loadKey() -> anyhow::Result<Vec<u8>> {
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700134/// if (good_but_auth_required) {
Janis Danisevskis8ea5f552020-11-20 11:22:59 -0800135/// Ok(vec!['k', 'e', 'y'])
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700136/// } else {
Janis Danisevskis8ea5f552020-11-20 11:22:59 -0800137/// Err(anyhow!(Error::Rc(ResponseCode::KEY_NOT_FOUND)))
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700138/// }
139/// }
140///
Janis Danisevskis8ea5f552020-11-20 11:22:59 -0800141/// map_or_log_err(loadKey(), Ok)
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700142/// ```
Janis Danisevskise24f3472020-08-12 17:58:49 -0700143pub fn map_or_log_err<T, U, F>(result: anyhow::Result<U>, handle_ok: F) -> BinderResult<T>
144where
145 F: FnOnce(U) -> BinderResult<T>,
146{
Janis Danisevskis778245c2021-03-04 15:40:23 -0800147 map_err_with(
148 result,
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700149 |e| {
Hasini Gunasinghee1d1bbd2021-04-20 18:13:25 +0000150 // Make the key not found errors silent.
151 if !matches!(
152 e.root_cause().downcast_ref::<Error>(),
153 Some(Error::Rc(ResponseCode::KEY_NOT_FOUND))
154 ) {
155 log::error!("{:?}", e);
156 }
Janis Danisevskis778245c2021-03-04 15:40:23 -0800157 e
158 },
159 handle_ok,
160 )
161}
162
Janis Danisevskisea03cff2021-12-16 08:10:17 -0800163/// This function turns an anyhow error into an optional CString.
164/// This is especially useful to add a message string to a service specific error.
165/// If the formatted string was not convertible because it contained a nul byte,
166/// None is returned and a warning is logged.
167pub fn anyhow_error_to_cstring(e: &anyhow::Error) -> Option<CString> {
168 match CString::new(format!("{:?}", e)) {
169 Ok(msg) => Some(msg),
170 Err(_) => {
171 log::warn!("Cannot convert error message to CStr. It contained a nul byte.");
172 None
173 }
174 }
175}
176
Janis Danisevskis778245c2021-03-04 15:40:23 -0800177/// This function behaves similar to map_or_log_error, but it does not log the errors, instead
178/// it calls map_err on the error before mapping it to a binder result allowing callers to
179/// log or transform the error before mapping it.
180pub fn map_err_with<T, U, F1, F2>(
181 result: anyhow::Result<U>,
182 map_err: F1,
183 handle_ok: F2,
184) -> BinderResult<T>
185where
186 F1: FnOnce(anyhow::Error) -> anyhow::Error,
187 F2: FnOnce(U) -> BinderResult<T>,
188{
189 result.map_or_else(
190 |e| {
191 let e = map_err(e);
Tri Vocd6fc7a2023-08-31 11:46:32 -0400192 let rc = anyhow_error_to_serialized_error(&e);
Janis Danisevskisea03cff2021-12-16 08:10:17 -0800193 Err(BinderStatus::new_service_specific_error(
Tri Vocd6fc7a2023-08-31 11:46:32 -0400194 rc.0,
Janis Danisevskisea03cff2021-12-16 08:10:17 -0800195 anyhow_error_to_cstring(&e).as_deref(),
196 ))
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700197 },
198 handle_ok,
199 )
200}
201
Tri Vocd6fc7a2023-08-31 11:46:32 -0400202/// This type is used to send error codes on the wire.
203///
204/// Errors are squashed into one number space using following rules:
205/// - All Keystore and Keymint errors codes are identity mapped. It's possible because by
206/// convention Keystore `ResponseCode` errors are positive, and Keymint `ErrorCode` errors are
207/// negative.
208/// - `selinux::Error::PermissionDenied` is mapped to `ResponseCode::PERMISSION_DENIED`.
209/// - All other error conditions, e.g. Binder errors, are mapped to `ResponseCode::SYSTEM_ERROR`.
210///
211/// The type should be used to forward all error codes to clients of Keystore AIDL interface and to
212/// metrics events.
213#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
214pub struct SerializedError(pub i32);
215
216/// Returns a SerializedError given a reference to Error.
217pub fn error_to_serialized_error(e: &Error) -> SerializedError {
218 match e {
219 Error::Rc(rcode) => SerializedError(rcode.0),
220 Error::Km(ec) => SerializedError(ec.0),
221 // Binder errors are reported as system error.
222 Error::Binder(_, _) | Error::BinderTransaction(_) => {
223 SerializedError(ResponseCode::SYSTEM_ERROR.0)
224 }
225 }
226}
227
228/// Returns a SerializedError given a reference to anyhow::Error.
229pub fn anyhow_error_to_serialized_error(e: &anyhow::Error) -> SerializedError {
Hasini Gunasingheb7142972021-02-20 03:11:27 +0000230 let root_cause = e.root_cause();
231 match root_cause.downcast_ref::<Error>() {
Tri Vocd6fc7a2023-08-31 11:46:32 -0400232 Some(e) => error_to_serialized_error(e),
Hasini Gunasingheb7142972021-02-20 03:11:27 +0000233 None => match root_cause.downcast_ref::<selinux::Error>() {
Tri Vocd6fc7a2023-08-31 11:46:32 -0400234 Some(selinux::Error::PermissionDenied) => {
235 SerializedError(ResponseCode::PERMISSION_DENIED.0)
236 }
237 _ => SerializedError(ResponseCode::SYSTEM_ERROR.0),
Hasini Gunasingheb7142972021-02-20 03:11:27 +0000238 },
239 }
240}
241
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700242#[cfg(test)]
Hasini Gunasingheaf993662020-07-24 18:40:20 +0000243pub mod tests {
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700244
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700245 use super::*;
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700246 use android_system_keystore2::binder::{
Janis Danisevskis017d2092020-09-02 10:15:52 -0700247 ExceptionCode, Result as BinderResult, Status as BinderStatus,
248 };
Janis Danisevskise24f3472020-08-12 17:58:49 -0700249 use anyhow::{anyhow, Context};
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700250
Janis Danisevskise24f3472020-08-12 17:58:49 -0700251 fn nested_nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700252 Err(anyhow!(Error::Rc(rc))).context("nested nested rc")
253 }
254
Janis Danisevskise24f3472020-08-12 17:58:49 -0700255 fn nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700256 nested_nested_rc(rc).context("nested rc")
257 }
258
259 fn nested_nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
260 Err(anyhow!(Error::Km(ec))).context("nested nested ec")
261 }
262
263 fn nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
264 nested_nested_ec(ec).context("nested ec")
265 }
266
Janis Danisevskise24f3472020-08-12 17:58:49 -0700267 fn nested_nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700268 Ok(rc)
269 }
270
Janis Danisevskise24f3472020-08-12 17:58:49 -0700271 fn nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700272 nested_nested_ok(rc).context("nested ok")
273 }
274
Janis Danisevskisce995432020-07-21 12:22:34 -0700275 fn nested_nested_selinux_perm() -> anyhow::Result<()> {
276 Err(anyhow!(selinux::Error::perm())).context("nested nexted selinux permission denied")
277 }
278
279 fn nested_selinux_perm() -> anyhow::Result<()> {
280 nested_nested_selinux_perm().context("nested selinux permission denied")
281 }
282
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700283 #[derive(Debug, thiserror::Error)]
284 enum TestError {
285 #[error("TestError::Fail")]
286 Fail = 0,
287 }
288
289 fn nested_nested_other_error() -> anyhow::Result<()> {
290 Err(anyhow!(TestError::Fail)).context("nested nested other error")
291 }
292
293 fn nested_other_error() -> anyhow::Result<()> {
294 nested_nested_other_error().context("nested other error")
295 }
296
Janis Danisevskis017d2092020-09-02 10:15:52 -0700297 fn binder_sse_error(sse: i32) -> BinderResult<()> {
298 Err(BinderStatus::new_service_specific_error(sse, None))
299 }
300
301 fn binder_exception(ex: ExceptionCode) -> BinderResult<()> {
302 Err(BinderStatus::new_exception(ex, None))
303 }
304
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700305 #[test]
306 fn keystore_error_test() -> anyhow::Result<(), String> {
307 android_logger::init_once(
308 android_logger::Config::default()
309 .with_tag("keystore_error_tests")
310 .with_min_level(log::Level::Debug),
311 );
Janis Danisevskise24f3472020-08-12 17:58:49 -0700312 // All Error::Rc(x) get mapped on a service specific error
313 // code of x.
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700314 for rc in ResponseCode::LOCKED.0..ResponseCode::BACKEND_BUSY.0 {
Janis Danisevskise24f3472020-08-12 17:58:49 -0700315 assert_eq!(
316 Result::<(), i32>::Err(rc),
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700317 map_or_log_err(nested_rc(ResponseCode(rc)), |_| Err(BinderStatus::ok()))
Janis Danisevskise24f3472020-08-12 17:58:49 -0700318 .map_err(|s| s.service_specific_error())
319 );
320 }
321
Janis Danisevskis017d2092020-09-02 10:15:52 -0700322 // All Keystore Error::Km(x) get mapped on a service
Janis Danisevskise24f3472020-08-12 17:58:49 -0700323 // specific error of x.
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700324 for ec in ErrorCode::UNKNOWN_ERROR.0..ErrorCode::ROOT_OF_TRUST_ALREADY_SET.0 {
Janis Danisevskise24f3472020-08-12 17:58:49 -0700325 assert_eq!(
326 Result::<(), i32>::Err(ec),
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700327 map_or_log_err(nested_ec(ErrorCode(ec)), |_| Err(BinderStatus::ok()))
Janis Danisevskise24f3472020-08-12 17:58:49 -0700328 .map_err(|s| s.service_specific_error())
329 );
330 }
331
Janis Danisevskis017d2092020-09-02 10:15:52 -0700332 // All Keymint errors x received through a Binder Result get mapped on
333 // a service specific error of x.
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700334 for ec in ErrorCode::UNKNOWN_ERROR.0..ErrorCode::ROOT_OF_TRUST_ALREADY_SET.0 {
Janis Danisevskis017d2092020-09-02 10:15:52 -0700335 assert_eq!(
336 Result::<(), i32>::Err(ec),
337 map_or_log_err(
338 map_km_error(binder_sse_error(ec))
339 .with_context(|| format!("Km error code: {}.", ec)),
340 |_| Err(BinderStatus::ok())
341 )
342 .map_err(|s| s.service_specific_error())
343 );
344 }
345
346 // map_km_error creates an Error::Binder variant storing
347 // ExceptionCode::SERVICE_SPECIFIC and the given
348 // service specific error.
349 let sse = map_km_error(binder_sse_error(1));
350 assert_eq!(Err(Error::Binder(ExceptionCode::SERVICE_SPECIFIC, 1)), sse);
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700351 // map_or_log_err then maps it on a service specific error of ResponseCode::SYSTEM_ERROR.
Janis Danisevskis017d2092020-09-02 10:15:52 -0700352 assert_eq!(
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700353 Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
Janis Danisevskis017d2092020-09-02 10:15:52 -0700354 map_or_log_err(sse.context("Non negative service specific error."), |_| Err(
355 BinderStatus::ok()
356 ))
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700357 .map_err(|s| ResponseCode(s.service_specific_error()))
Janis Danisevskis017d2092020-09-02 10:15:52 -0700358 );
359
360 // map_km_error creates a Error::Binder variant storing the given exception code.
361 let binder_exception = map_km_error(binder_exception(ExceptionCode::TRANSACTION_FAILED));
362 assert_eq!(Err(Error::Binder(ExceptionCode::TRANSACTION_FAILED, 0)), binder_exception);
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700363 // map_or_log_err then maps it on a service specific error of ResponseCode::SYSTEM_ERROR.
Janis Danisevskis017d2092020-09-02 10:15:52 -0700364 assert_eq!(
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700365 Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
Janis Danisevskis017d2092020-09-02 10:15:52 -0700366 map_or_log_err(binder_exception.context("Binder Exception."), |_| Err(
367 BinderStatus::ok()
368 ))
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700369 .map_err(|s| ResponseCode(s.service_specific_error()))
Janis Danisevskis017d2092020-09-02 10:15:52 -0700370 );
371
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700372 // selinux::Error::Perm() needs to be mapped to ResponseCode::PERMISSION_DENIED
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700373 assert_eq!(
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700374 Result::<(), ResponseCode>::Err(ResponseCode::PERMISSION_DENIED),
Janis Danisevskise24f3472020-08-12 17:58:49 -0700375 map_or_log_err(nested_selinux_perm(), |_| Err(BinderStatus::ok()))
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700376 .map_err(|s| ResponseCode(s.service_specific_error()))
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700377 );
378
Janis Danisevskise24f3472020-08-12 17:58:49 -0700379 // All other errors get mapped on System Error.
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700380 assert_eq!(
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700381 Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
Janis Danisevskise24f3472020-08-12 17:58:49 -0700382 map_or_log_err(nested_other_error(), |_| Err(BinderStatus::ok()))
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700383 .map_err(|s| ResponseCode(s.service_specific_error()))
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700384 );
385
386 // Result::Ok variants get passed to the ok handler.
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700387 assert_eq!(Ok(ResponseCode::LOCKED), map_or_log_err(nested_ok(ResponseCode::LOCKED), Ok));
388 assert_eq!(
389 Ok(ResponseCode::SYSTEM_ERROR),
390 map_or_log_err(nested_ok(ResponseCode::SYSTEM_ERROR), Ok)
391 );
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700392
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700393 Ok(())
394 }
Hasini Gunasingheaf993662020-07-24 18:40:20 +0000395
396 //Helper function to test whether error cases are handled as expected.
Janis Danisevskise24f3472020-08-12 17:58:49 -0700397 pub fn check_result_contains_error_string<T>(
398 result: anyhow::Result<T>,
399 expected_error_string: &str,
400 ) {
Hasini Gunasingheaf993662020-07-24 18:40:20 +0000401 let error_str = format!(
402 "{:#?}",
403 result.err().unwrap_or_else(|| panic!("Expected the error: {}", expected_error_string))
404 );
405 assert!(
406 error_str.contains(expected_error_string),
407 "The string \"{}\" should contain \"{}\"",
408 error_str,
409 expected_error_string
410 );
411 }
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700412} // mod tests