Skip to main content

gix_error/exn/
ext.rs

1// Copyright 2025 FastLabs Developers
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
15use crate::exn::Exn;
16
17/// A trait bound of the supported error type of [`Exn`].
18pub trait ErrorExt: std::error::Error + Send + Sync + 'static {
19    /// Raise this error as a new exception.
20    #[track_caller]
21    fn raise(self) -> Exn<Self>
22    where
23        Self: Sized,
24    {
25        Exn::new(self)
26    }
27
28    /// Raise this error as a child of a new exception with the given context error.
29    ///
30    /// This is a shorthand for `self.raise().raise(context)` — it wraps `self` in an [`Exn`]
31    /// and immediately nests it under a new `Exn<T>` headed by `context`.
32    ///
33    /// ```rust,ignore
34    /// // Instead of:
35    /// io_err.raise().raise(message("could not read file"))
36    ///
37    /// // Write:
38    /// io_err.and_raise(message("could not read file"))
39    /// ```
40    #[track_caller]
41    fn and_raise<T: std::error::Error + Send + Sync + 'static>(self, context: T) -> Exn<T>
42    where
43        Self: Sized,
44    {
45        Exn::new(self).raise(context)
46    }
47
48    /// Raise this error as a new exception, with type erasure.
49    #[track_caller]
50    fn raise_erased(self) -> Exn
51    where
52        Self: Sized,
53    {
54        Exn::new(self).erased()
55    }
56
57    /// Raise this error as a new exception, with `sources` as causes.
58    #[track_caller]
59    fn raise_all<T, I>(self, sources: I) -> Exn<Self>
60    where
61        Self: Sized,
62        T: std::error::Error + Send + Sync + 'static,
63        I: IntoIterator,
64        I::Item: Into<Exn<T>>,
65    {
66        Exn::raise_all(sources, self)
67    }
68}
69
70impl<T> ErrorExt for T where T: std::error::Error + Send + Sync + 'static {}
71
72/// An extension trait for [`Option`] to provide raising new exceptions on `None`.
73pub trait OptionExt {
74    /// The `Some` type.
75    type Some;
76
77    /// Construct a new [`Exn`] on the `None` variant.
78    fn ok_or_raise<A, F>(self, err: F) -> Result<Self::Some, Exn<A>>
79    where
80        A: std::error::Error + Send + Sync + 'static,
81        F: FnOnce() -> A;
82
83    /// Construct a new [`Exn`] on the `None` variant, with type erasure.
84    fn ok_or_raise_erased<A, F>(self, err: F) -> Result<Self::Some, Exn>
85    where
86        A: std::error::Error + Send + Sync + 'static,
87        F: FnOnce() -> A;
88}
89
90impl<T> OptionExt for Option<T> {
91    type Some = T;
92
93    #[track_caller]
94    fn ok_or_raise<A, F>(self, err: F) -> Result<T, Exn<A>>
95    where
96        A: std::error::Error + Send + Sync + 'static,
97        F: FnOnce() -> A,
98    {
99        match self {
100            Some(v) => Ok(v),
101            None => Err(Exn::new(err())),
102        }
103    }
104
105    #[track_caller]
106    fn ok_or_raise_erased<A, F>(self, err: F) -> Result<T, Exn>
107    where
108        A: std::error::Error + Send + Sync + 'static,
109        F: FnOnce() -> A,
110    {
111        self.ok_or_raise(err).map_err(Exn::erased)
112    }
113}
114
115/// An extension trait for [`Result`] to provide context information on [`Exn`]s.
116pub trait ResultExt {
117    /// The `Ok` type.
118    type Success;
119
120    /// The `Err` type that would be wrapped in an [`Exn`].
121    type Error: std::error::Error + Send + Sync + 'static;
122
123    /// Raise a new exception on the [`Exn`] inside the [`Result`].
124    ///
125    /// Apply [`Exn::raise`] on the `Err` variant, refer to it for more information.
126    fn or_raise<A, F>(self, err: F) -> Result<Self::Success, Exn<A>>
127    where
128        A: std::error::Error + Send + Sync + 'static,
129        F: FnOnce() -> A;
130
131    /// Raise a new exception on the [`Exn`] inside the [`Result`], but erase its type.
132    ///
133    /// Apply [`Exn::erased`] on the `Err` variant, refer to it for more information.
134    fn or_erased(self) -> Result<Self::Success, Exn>;
135
136    /// Raise a new exception on the [`Exn`] inside the [`Result`], and type-erase the result.
137    ///
138    /// Apply [`Exn::raise`] and [`Exn::erased`] on the `Err` variant, refer to it for more information.
139    fn or_raise_erased<A, F>(self, err: F) -> Result<Self::Success, Exn>
140    where
141        A: std::error::Error + Send + Sync + 'static,
142        F: FnOnce() -> A;
143}
144
145impl<T, E> ResultExt for Result<T, E>
146where
147    E: std::error::Error + Send + Sync + 'static,
148{
149    type Success = T;
150    type Error = E;
151
152    #[track_caller]
153    fn or_raise<A, F>(self, err: F) -> Result<Self::Success, Exn<A>>
154    where
155        A: std::error::Error + Send + Sync + 'static,
156        F: FnOnce() -> A,
157    {
158        match self {
159            Ok(v) => Ok(v),
160            Err(e) => Err(Exn::new(e).raise(err())),
161        }
162    }
163
164    #[track_caller]
165    fn or_erased(self) -> Result<Self::Success, Exn> {
166        match self {
167            Ok(v) => Ok(v),
168            Err(e) => Err(Exn::new(e).erased()),
169        }
170    }
171
172    #[track_caller]
173    fn or_raise_erased<A, F>(self, err: F) -> Result<Self::Success, Exn>
174    where
175        A: std::error::Error + Send + Sync + 'static,
176        F: FnOnce() -> A,
177    {
178        self.or_raise(err).map_err(Exn::erased)
179    }
180}
181
182impl<T, E> ResultExt for Result<T, Exn<E>>
183where
184    E: std::error::Error + Send + Sync + 'static,
185{
186    type Success = T;
187    type Error = E;
188
189    #[track_caller]
190    fn or_raise<A, F>(self, err: F) -> Result<Self::Success, Exn<A>>
191    where
192        A: std::error::Error + Send + Sync + 'static,
193        F: FnOnce() -> A,
194    {
195        match self {
196            Ok(v) => Ok(v),
197            Err(e) => Err(e.raise(err())),
198        }
199    }
200
201    #[track_caller]
202    fn or_erased(self) -> Result<Self::Success, Exn> {
203        match self {
204            Ok(v) => Ok(v),
205            Err(e) => Err(e.erased()),
206        }
207    }
208
209    #[track_caller]
210    fn or_raise_erased<A, F>(self, err: F) -> Result<Self::Success, Exn>
211    where
212        A: std::error::Error + Send + Sync + 'static,
213        F: FnOnce() -> A,
214    {
215        self.or_raise(err).map_err(Exn::erased)
216    }
217}