mz_ore/
result.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
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 in the LICENSE file at the
6// root of this repository, or online at
7//
8//     http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Result utilities.
17
18use std::convert::Infallible;
19
20use crate::error::ErrorExt;
21
22/// Extension methods for [`std::result::Result`].
23pub trait ResultExt<T, E> {
24    /// Applies [`Into::into`] to a contained [`Err`] value, leaving an [`Ok`]
25    /// value untouched.
26    fn err_into<E2>(self) -> Result<T, E2>
27    where
28        E: Into<E2>;
29
30    /// Formats an [`Err`] value as a detailed error message, preserving any context information.
31    ///
32    /// This is equivalent to `format!("{}", err.display_with_causes())`, except that it's easier to
33    /// type.
34    fn err_to_string_with_causes(&self) -> Option<String>
35    where
36        E: std::error::Error;
37
38    /// Maps a `Result<T, E>` to `Result<T, String>` by converting the [`Err`] result into a string,
39    /// along with the chain of source errors, if any.
40    fn map_err_to_string_with_causes(self) -> Result<T, String>
41    where
42        E: std::error::Error;
43
44    /// Safely unwraps a `Result<T, Infallible>`, where [`Infallible`] is a type that represents when
45    /// an error cannot occur.
46    fn infallible_unwrap(self) -> T
47    where
48        E: Into<Infallible>;
49}
50
51impl<T, E> ResultExt<T, E> for Result<T, E> {
52    fn err_into<E2>(self) -> Result<T, E2>
53    where
54        E: Into<E2>,
55    {
56        self.map_err(|e| e.into())
57    }
58
59    fn err_to_string_with_causes(&self) -> Option<String>
60    where
61        E: std::error::Error,
62    {
63        self.as_ref().err().map(ErrorExt::to_string_with_causes)
64    }
65
66    fn map_err_to_string_with_causes(self) -> Result<T, String>
67    where
68        E: std::error::Error,
69    {
70        self.map_err(|e| ErrorExt::to_string_with_causes(&e))
71    }
72
73    fn infallible_unwrap(self) -> T
74    where
75        E: Into<Infallible>,
76    {
77        match self {
78            Ok(t) => t,
79            Err(e) => {
80                let _infallible = e.into();
81
82                // This code will forever be unreachable because Infallible is an enum
83                // with no variants, so it's impossible to construct. If it ever does
84                // become possible to construct this will become a compile time error
85                // since there will be a variant we're not matching on.
86                #[allow(unreachable_code)]
87                match _infallible {}
88            }
89        }
90    }
91}
92
93#[cfg(test)]
94mod test {
95    use std::fmt::Display;
96
97    use anyhow::anyhow;
98
99    use super::*;
100
101    #[crate::test]
102    fn prints_error_chain() {
103        let error = anyhow!("root");
104        let error = error.context("context");
105        let error = TestError { inner: error };
106        let res: Result<(), _> = Err(error);
107
108        assert_eq!(
109            res.err_to_string_with_causes(),
110            Some("test error: context: root".to_string())
111        );
112
113        let error = anyhow!("root");
114        let error = error.context("context");
115        let error = TestError { inner: error };
116        let res: Result<(), _> = Err(error);
117
118        assert_eq!(
119            res.map_err_to_string_with_causes(),
120            Err("test error: context: root".to_string())
121        );
122    }
123
124    #[derive(Debug)]
125    struct TestError {
126        inner: anyhow::Error,
127    }
128
129    impl Display for TestError {
130        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131            // We don't print our causes.
132            write!(f, "test error")?;
133            Ok(())
134        }
135    }
136
137    impl std::error::Error for TestError {
138        fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
139            Some(self.inner.as_ref())
140        }
141    }
142}