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