realloc/
strategy.rs

1pub(crate) const FAILURE_MESSAGE: &str = "failed to allocate";
2
3/// A method for handling allocation failure.
4///
5/// All implementors, while not strictly required, are expected to be
6/// zero-sized.
7///
8/// While you can create your own (likely through
9/// [`strategy!`](crate::strategy)), it's recommended to use one of [`Abort`],
10/// [`Panic`], [`Optional`], or [`Fallible`].
11pub trait Strategy {
12    /// The type this strategy returns from fallible methods.
13    type Result<T, E>;
14
15    /// The "unit value" for this type.
16    ///
17    /// Used to store it in collections.
18    const UNIT: Self;
19
20    /// Transform a full `Result` into the desired output.
21    ///
22    /// This is the core of the trait; it's what actually performs the error
23    /// handling this strategy prescribes.
24    fn create<T, E>(value: Result<T, E>) -> Self::Result<T, E>;
25
26    /// Transform an ok result of one type to an ok result of another.
27    fn map<T, E, U, F>(value: Self::Result<T, E>, f: F) -> Self::Result<U, E>
28    where
29        F: FnOnce(T) -> U;
30
31    /// Transform an error result of one type to an error result of another.
32    fn map_err<T, E, V, F>(value: Self::Result<T, E>, f: F) -> Self::Result<T, V>
33    where
34        F: FnOnce(E) -> V;
35
36    /// If `value` is an ok result, transform it through `f`, flattening the
37    /// results.
38    fn and_then<T, E, U, F>(value: Self::Result<T, E>, f: F) -> Self::Result<U, E>
39    where
40        F: FnOnce(T) -> Self::Result<U, E>;
41
42    /// Create an ok result from a value.
43    fn ok<T, E>(value: T) -> Self::Result<T, E> {
44        Self::create(Ok(value))
45    }
46
47    /// Create an error result from an error.
48    fn err<T, E>(error: E) -> Self::Result<T, E> {
49        Self::create(Err(error))
50    }
51
52    /// If `value` is an ok result, run `f` on it, returning `value` unchanged.
53    fn inspect<T, E, F>(value: Self::Result<T, E>, f: F) -> Self::Result<T, E>
54    where
55        F: FnOnce(&T),
56    {
57        Self::and_then(value, |value| {
58            f(&value);
59            Self::ok(value)
60        })
61    }
62}
63
64/// Create a custom [`Strategy`].
65///
66/// # Examples
67///
68/// ```
69/// # struct Logger;
70/// # impl Logger { const fn new() -> Self { Self } fn log<E>(&self, _: &str, _: &E) {} }
71///
72/// strategy! {
73///     // The name and visibility of the strategy, plus whatever docs/attrs you
74///     // want to put on it.
75///     //
76///     // If it's not a unit struct, it has to have a `const` default value.
77///     #[derive(Debug, Copy)]
78///     pub struct Log(Logger) = Log(Logger::new());
79///
80///     // The result of applying the transformation. In this case, we're just
81///     // logging, so we don't want to remove the errors.
82///     type Result<T, E> = Result<T, E>;
83///
84///     // How to transform an `Ok`/`Err` value to an ok/error result of our
85///     // `Result` type. In our case, we don't want to change anything, just
86///     // log any errors.
87///     Ok(val) => Ok(val);
88///     Err(err) => {
89///         self.0.log("encountered an error", &err);
90///         Err(err)
91///     };
92///
93///     // How to apply various transformations to our result type. Since this
94///     // is just a plain `Result`, we can delegate to its implementations.
95///     fn map(value, f) => value.map(f);
96///     fn map_err(value, f) => value.map_err(f);
97///     fn and_then(value, f) => value.and_then(f);
98/// }
99/// ```
100#[macro_export]
101macro_rules! strategy {
102    (
103        $(#[$attr:meta])*
104        $vis:vis struct $name:ident
105            $( ($($tuple_fields:tt)*) = $tuple_unit:expr; )?
106            $( {$($struct_fields:tt)*} = $struct_unit:expr; )?
107            $(;)?
108
109        type Result<$t:ident, $e:ident> = $output:ty;
110
111        Ok($ok_val:pat) => $ok:expr;
112        Err($err_val:pat) => $err:expr;
113
114        fn map($map_val:pat, $map_f:pat) => $map:expr;
115        fn map_err($map_err_val:pat, $map_err_f:pat) => $map_err:expr;
116        fn and_then($and_then_val:pat, $and_then_f:pat) => $and_then:expr;
117    ) => {
118        $(#[$attr])*
119        $vis struct $name
120            $( ($($tuple_fields)*) )?
121            // in order to make the trailing semicolon acceptable, we must
122            // insert an item here that will consume it without causing
123            // potential conflicts
124            $( {$($struct_fields)*} const _: () = () )?
125        ;
126
127        impl $crate::Strategy for $name {
128            type Result<$t, $e> = $output;
129
130            const UNIT: Self = $crate::__if_else!([
131                $($tuple_unit)?
132            ] || [
133                $($struct_unit)?
134            ] else [
135                Self
136            ] else "must either be a tuple struct or regular struct, not both"
137            );
138
139            fn create<$t, $e>(
140                __strategy_impl_value: ::core::result::Result<$t, $e>
141            ) -> <Self as $crate::Strategy>::Result<$t, $e> {
142                match __strategy_impl_value {
143                    ::core::result::Result::Ok($ok_val) => $ok,
144                    ::core::result::Result::Err($err_val) => $err,
145                }
146            }
147
148            #[allow(non_camel_case_types)]
149            fn map<$t, $e, __STRATEGY_IMPL_U, __STRATEGY_IMPL_F>(
150                $map_val: <Self as $crate::Strategy>::Result<$t, $e>,
151                $map_f: __STRATEGY_IMPL_F,
152            ) -> <Self as $crate::Strategy>::Result<__STRATEGY_IMPL_U, $e>
153            where
154                __STRATEGY_IMPL_F: ::core::ops::FnOnce($t) -> __STRATEGY_IMPL_U,
155            {
156                $map
157            }
158
159            #[allow(non_camel_case_types)]
160            fn map_err<$t, $e, __STRATEGY_IMPL_V, __STRATEGY_IMPL_F>(
161                $map_err_val: <Self as $crate::Strategy>::Result<$t, $e>,
162                $map_err_f: __STRATEGY_IMPL_F,
163            ) -> <Self as $crate::Strategy>::Result<$t, __STRATEGY_IMPL_V>
164            where
165                __STRATEGY_IMPL_F: core::ops::FnOnce($e) -> __STRATEGY_IMPL_V,
166            {
167                $map_err
168            }
169
170            #[allow(non_camel_case_types)]
171            fn and_then<$t, $e, __STRATEGY_IMPL_U, __STRATEGY_IMPL_F>(
172                $and_then_val: <Self as $crate::Strategy>::Result<$t, $e>,
173                $and_then_f: __STRATEGY_IMPL_F,
174            ) -> <Self as $crate::Strategy>::Result<__STRATEGY_IMPL_U, $e>
175            where
176                __STRATEGY_IMPL_F: core::ops::FnOnce(T)
177                    -> <Self as $crate::Strategy>::Result<__STRATEGY_IMPL_U, $e>,
178            {
179                $and_then
180            }
181        }
182    }
183}
184
185/// A "true" abort, without relying on `core::intrinsics::abort` or pulling in
186/// the panic machinery of `unreachable!`.
187fn abort() -> ! {
188    // SAFETY:
189    // - the assembly does not violate Rust's AM
190    // - the assembly guarantees `unreachable_unchecked` will never be reached
191    unsafe {
192        core::arch::asm!("ud2");
193        core::hint::unreachable_unchecked()
194    }
195}
196
197strategy! {
198    /// A [`Strategy`] prescribing that the program immediately abort on
199    /// failure.
200    ///
201    /// See also [`Panic`].
202    pub struct Abort;
203
204    type Result<T, E> = T;
205
206    Ok(v) => v;
207    Err(_) => abort();
208
209    fn map(v, f) => f(v);
210    fn map_err(v, _) => v;
211    fn and_then(v, f) => f(v);
212}
213
214strategy! {
215    /// A [`Strategy`] prescribing that the program panic on failure.
216    ///
217    /// See also [`Abort`] and [`Fallible`].
218    pub struct Panic;
219
220    type Result<T, E> = T;
221
222    Ok(v) => v;
223    Err(_) => panic!("{}", FAILURE_MESSAGE);
224
225    fn map(v, f) => f(v);
226    fn map_err(v, _) => v;
227    fn and_then(v, f) => f(v);
228}
229
230strategy! {
231    /// A [`Strategy`] prescribing that errors be discarded, and `Option`s
232    /// returned instead.
233    ///
234    /// See also [`Fallible`] and [`Panic`].
235    pub struct Optional;
236
237    type Result<T, E> = Option<T>;
238
239    Ok(v) => Some(v);
240    Err(_) => None;
241
242    fn map(v, f) => v.map(f);
243    fn map_err(v, _) => v;
244    fn and_then(v, f) => v.and_then(f);
245}
246
247strategy! {
248    /// A [`Strategy`] prescribing that all fallible methods return `Result`s.
249    ///
250    /// See also [`Optional`] and [`Panic`].
251    pub struct Fallible;
252
253    type Result<T, E> = Result<T, E>;
254
255    Ok(v) => Ok(v);
256    Err(e) => Err(e);
257
258    fn map(v, f) => v.map(f);
259    fn map_err(v, f) => v.map_err(f);
260    fn and_then(v, f) => v.and_then(f);
261}
262
263#[macro_export]
264#[doc(hidden)]
265macro_rules! __if_else {
266    ([] || [] else [$($t:tt)*] else $err:literal) => {$($t)*};
267    ([] || [$($t:tt)*] else [$($_:tt)*] else $err:literal) => {$($t)*};
268    ([$($t:tt)*] || [] else [$($_:tt)*] else $err:literal) => {$($t)*};
269    ([$($_:tt)*] || [$($__:tt)*] else [$($___:tt)*] else $err:literal) => {
270        compile_error!($err);
271    };
272
273    ([] else [$($t:tt)*]) => {$($t)*};
274    ([$($t:tt)*] else [$($_:tt)*]) => {$($t)*};
275}