dcrypt_api/error/
traits.rs1use super::registry::ERROR_REGISTRY;
4use super::types::{Error, Result};
5use dcrypt_internal::constant_time::ConditionallySelectable;
6
7pub trait ResultExt<T, E>: Sized {
9 fn wrap_err<F, E2>(self, f: F) -> core::result::Result<T, E2>
11 where
12 F: FnOnce() -> E2;
13
14 fn with_context(self, context: &'static str) -> Result<T>
16 where
17 E: Into<Error>;
18
19 #[cfg(feature = "std")]
20 fn with_message(self, message: impl Into<String>) -> Result<T>
22 where
23 E: Into<Error>;
24}
25
26impl<T, E> ResultExt<T, E> for core::result::Result<T, E> {
27 fn wrap_err<F, E2>(self, f: F) -> core::result::Result<T, E2>
28 where
29 F: FnOnce() -> E2,
30 {
31 self.map_err(|_| f())
32 }
33
34 fn with_context(self, context: &'static str) -> Result<T>
35 where
36 E: Into<Error>,
37 {
38 self.map_err(|e| {
39 let err = e.into();
40 err.with_context(context)
41 })
42 }
43
44 #[cfg(feature = "std")]
45 fn with_message(self, message: impl Into<String>) -> Result<T>
46 where
47 E: Into<Error>,
48 {
49 self.map_err(|e| {
50 let err = e.into();
51 err.with_message(message)
52 })
53 }
54}
55
56pub trait ErrorRegistryExt<T, E>: Sized {
62 fn unwrap_or_record_with<F>(self, default: T, on_error: F) -> T
64 where
65 F: FnOnce() -> E,
66 E: Send + 'static;
67}
68
69impl<T, E> ErrorRegistryExt<T, E> for core::result::Result<T, E> {
70 fn unwrap_or_record_with<F>(self, default: T, on_error: F) -> T
71 where
72 F: FnOnce() -> E,
73 E: Send + 'static,
74 {
75 match self {
76 Ok(value) => value,
77 Err(_) => {
78 ERROR_REGISTRY.store(on_error());
79 default
80 }
81 }
82 }
83}
84
85pub trait SecureErrorHandling<T, E>: Sized {
91 #[deprecated(
96 note = "secure_unwrap is not constant-time; use ErrorRegistryExt::unwrap_or_record_with for non-secret control flow"
97 )]
98 fn secure_unwrap<F>(self, default: T, on_error: F) -> T
99 where
100 F: FnOnce() -> E,
101 E: Send + 'static;
102}
103
104#[allow(deprecated)]
105impl<T, E> SecureErrorHandling<T, E> for core::result::Result<T, E> {
106 fn secure_unwrap<F>(self, default: T, on_error: F) -> T
107 where
108 F: FnOnce() -> E,
109 E: Send + 'static,
110 {
111 self.unwrap_or_record_with(default, on_error)
112 }
113}
114
115pub trait ConstantTimeResult<T, E> {
122 #[deprecated(
124 note = "ct_is_ok branches on the Result variant; use Result::is_ok and do not treat the variant as secret"
125 )]
126 fn ct_is_ok(&self) -> bool;
127
128 #[deprecated(
130 note = "ct_is_err branches on the Result variant; use Result::is_err and do not treat the variant as secret"
131 )]
132 fn ct_is_err(&self) -> bool;
133
134 #[deprecated(
139 note = "ct_map invokes only the selected closure; use Result::map_or_else and do not treat the variant as secret"
140 )]
141 fn ct_map<U, F, G>(self, ok_fn: F, err_fn: G) -> U
142 where
143 F: FnOnce(T) -> U,
144 G: FnOnce(E) -> U,
145 U: ConditionallySelectable;
146}
147
148#[allow(deprecated)]
149impl<T, E> ConstantTimeResult<T, E> for core::result::Result<T, E> {
150 fn ct_is_ok(&self) -> bool {
151 self.is_ok()
152 }
153
154 fn ct_is_err(&self) -> bool {
155 self.is_err()
156 }
157
158 fn ct_map<U, F, G>(self, ok_fn: F, err_fn: G) -> U
159 where
160 F: FnOnce(T) -> U,
161 G: FnOnce(E) -> U,
162 U: ConditionallySelectable,
163 {
164 match self {
165 Ok(value) => ok_fn(value),
166 Err(error) => err_fn(error),
167 }
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::ErrorRegistryExt;
174 use crate::error::ERROR_REGISTRY;
175 use core::sync::atomic::{AtomicUsize, Ordering};
176
177 #[test]
178 fn record_extension_invokes_error_factory_only_on_error() {
179 static CALLS: AtomicUsize = AtomicUsize::new(0);
180 ERROR_REGISTRY.clear();
181
182 let ok: core::result::Result<u8, &'static str> = Ok(7);
183 assert_eq!(
184 ok.unwrap_or_record_with(9, || {
185 CALLS.fetch_add(1, Ordering::SeqCst);
186 "recorded error"
187 }),
188 7
189 );
190 assert_eq!(CALLS.load(Ordering::SeqCst), 0);
191
192 let error: core::result::Result<u8, &'static str> = Err("source error");
193 assert_eq!(
194 error.unwrap_or_record_with(9, || {
195 CALLS.fetch_add(1, Ordering::SeqCst);
196 "recorded error"
197 }),
198 9
199 );
200 assert_eq!(CALLS.load(Ordering::SeqCst), 1);
201 ERROR_REGISTRY.clear();
202 }
203}