Skip to main content

ctutils/
ct_option.rs

1use crate::{Choice, CtAssign, CtAssignSlice, CtEq, CtEqSlice, CtSelect};
2use core::ops::{Deref, DerefMut};
3
4/// Helper macro for providing behavior like the [`CtOption::map`] combinator that works in
5/// `const fn` contexts.
6///
7/// Requires a provided `$mapper` function to convert from one type to another, e.g.
8///
9/// ```ignore
10/// const fn mapper(value: T) -> U
11/// ```
12#[macro_export]
13macro_rules! map {
14    ($opt:expr, $mapper:path) => {{ $crate::CtOption::new($mapper($opt.to_inner_unchecked()), $opt.is_some()) }};
15}
16
17/// Helper macro for providing behavior like the [`CtOption::unwrap_or`] combinator that works in
18/// `const fn` contexts.
19///
20/// Requires a provided selector function `$select` to perform constant-time selection which takes
21/// two `T` values by reference along with a [`Choice`], returning the first `T` for
22/// [`Choice::FALSE`], and the second for [`Choice::TRUE`], e.g.:
23///
24/// ```ignore
25/// const fn ct_select(a: &T, b: &T, condition: Choice) -> T
26/// ```
27#[macro_export]
28macro_rules! unwrap_or {
29    ($opt:expr, $default:expr, $select:path) => {
30        $select(&$default, $opt.as_inner_unchecked(), $opt.is_some())
31    };
32}
33
34/// Equivalent of [`Option`] but predicated on a [`Choice`] with combinators that allow for
35/// constant-time operations which always perform the same sequence of instructions regardless of
36/// the value of `is_some`.
37///
38/// Unlike [`Option`], [`CtOption`] always contains a value, and will use the contained value when
39/// e.g. evaluating the callbacks of combinator methods, which unlike `core` it does unconditionally
40/// in order to ensure constant-time operation. This approach stands in contrast to the lazy
41/// evaluation similar methods on [`Option`] provide.
42#[derive(Clone, Copy, Debug)]
43pub struct CtOption<T> {
44    value: T,
45    is_some: Choice,
46}
47
48impl<T> CtOption<T> {
49    /// Construct a new [`CtOption`], with a [`Choice`] parameter `is_some` as a stand-in for
50    /// `Some` or `None` enum variants of a typical [`Option`] type.
51    #[inline]
52    #[must_use]
53    pub const fn new(value: T, is_some: Choice) -> CtOption<T> {
54        Self { value, is_some }
55    }
56
57    /// Construct a new [`CtOption`] where `self.is_some()` is [`Choice::TRUE`].
58    #[inline]
59    #[must_use]
60    pub const fn some(value: T) -> CtOption<T> {
61        Self::new(value, Choice::TRUE)
62    }
63
64    /// Construct a new [`CtOption`] with the [`Default`] value, and where `self.is_some()` is
65    /// [`Choice::FALSE`].
66    #[inline]
67    #[must_use]
68    pub fn none() -> CtOption<T>
69    where
70        T: Default,
71    {
72        Self::new(Default::default(), Choice::FALSE)
73    }
74
75    /// Convert from a `&mut CtOption<T>` to `CtOption<&mut T>`.
76    #[inline]
77    #[must_use]
78    pub const fn as_mut(&mut self) -> CtOption<&mut T> {
79        CtOption {
80            value: &mut self.value,
81            is_some: self.is_some,
82        }
83    }
84
85    /// Convert from a `&CtOption<T>` to `CtOption<&T>`.
86    #[inline]
87    #[must_use]
88    pub const fn as_ref(&self) -> CtOption<&T> {
89        CtOption {
90            value: &self.value,
91            is_some: self.is_some,
92        }
93    }
94
95    /// Convert from `CtOption<T>` (or `&CtOption<T>`) to `CtOption<&T::Target>`, for types which
96    /// impl the [`Deref`] trait.
97    #[inline]
98    #[must_use]
99    pub fn as_deref(&self) -> CtOption<&T::Target>
100    where
101        T: Deref,
102    {
103        self.as_ref().map(Deref::deref)
104    }
105
106    /// Convert from `CtOption<T>` (or `&mut CtOption<T>`) to `CtOption<&mut T::Target>`, for types
107    /// which impl the [`DerefMut`] trait.
108    #[inline]
109    #[must_use]
110    pub fn as_deref_mut(&mut self) -> CtOption<&mut T::Target>
111    where
112        T: DerefMut,
113    {
114        self.as_mut().map(DerefMut::deref_mut)
115    }
116
117    /// Return the contained value, consuming the `self` value.
118    ///
119    /// # Panics
120    /// In the event `self.is_some()` is [`Choice::FALSE`], panics with a custom panic message
121    /// provided as the `msg` argument.
122    #[inline]
123    #[must_use]
124    #[track_caller]
125    pub fn expect(self, msg: &str) -> T {
126        assert!(self.is_some().to_bool(), "{}", msg);
127        self.value
128    }
129
130    /// Return the contained value, consuming the `self` value, with `const fn` support.
131    ///
132    /// Relies on a `Copy` bound which implies `!Drop` which is needed to be able to move out of
133    /// `self` in a `const fn` without `feature(const_precise_live_drops)`.
134    ///
135    /// # Panics
136    /// In the event `self.is_some()` is [`Choice::FALSE`], panics with a custom panic message
137    /// provided as the `msg` argument.
138    // TODO(tarcieri): get rid of this when we can make `expect` a `const fn`
139    // (needs `const_precise_live_drops`)
140    #[inline]
141    #[must_use]
142    #[track_caller]
143    pub const fn expect_copied(self, msg: &str) -> T
144    where
145        T: Copy,
146    {
147        *self.expect_ref(msg)
148    }
149
150    /// Borrow the contained value.
151    ///
152    /// # Panics
153    /// In the event `self.is_some()` is [`Choice::FALSE`], panics with a custom panic message
154    /// provided as the `msg` argument.
155    // TODO(tarcieri): get rid of this when we can make `expect` a `const fn`
156    // (needs `const_precise_live_drops`)
157    #[inline]
158    #[must_use]
159    #[track_caller]
160    pub const fn expect_ref(&self, msg: &str) -> &T {
161        assert!(self.is_some.to_bool(), "{}", msg);
162        self.as_inner_unchecked()
163    }
164
165    /// Inserts `value` into the [`CtOption`], then returns a mutable reference to it.
166    ///
167    /// If the option already contains a value, the old value is dropped.
168    pub fn insert(&mut self, value: T) -> &mut T {
169        self.value = value;
170        self.is_some = Choice::TRUE;
171        &mut self.value
172    }
173
174    /// Conditionally inserts `value` into the [`CtOption`] if the given condition holds.
175    pub fn insert_if(&mut self, value: &T, condition: Choice)
176    where
177        T: CtAssign,
178    {
179        self.value.ct_assign(value, condition);
180        self.is_some.ct_assign(&Choice::TRUE, condition);
181    }
182
183    /// Convert the [`CtOption`] wrapper into an [`Option`], depending on whether
184    /// [`CtOption::is_some`] is a truthy or falsy [`Choice`].
185    ///
186    /// This function exists to avoid ending up with ugly, verbose and/or bad handled conversions
187    /// from the [`CtOption`] wraps to an [`Option`] or [`Result`].
188    ///
189    /// It's equivalent to the corresponding [`From`] impl, however this version is friendlier for
190    /// type inference.
191    ///
192    /// <div class="warning">
193    /// <b>Warning: variable-time!</b>
194    ///
195    /// This implementation doesn't intend to be constant-time nor try to protect the leakage of the
196    /// `T` value since the [`Option`] will do it anyway.
197    /// </div>
198    #[inline]
199    pub fn into_option(self) -> Option<T> {
200        if self.is_some.to_bool() {
201            Some(self.value)
202        } else {
203            None
204        }
205    }
206
207    /// Convert the [`CtOption`] wrapper into an [`Option`] in a `const fn`-friendly manner.
208    ///
209    /// This is the equivalent of [`CtOption::into_option`] but is `const fn`-friendly by only
210    /// allowing `Copy` types which are implicitly `!Drop` and don't run into problems with
211    /// `const fn` and destructors.
212    ///
213    /// <div class="warning">
214    /// <b>Warning: variable-time!</b>
215    ///
216    /// This implementation doesn't intend to be constant-time nor try to protect the leakage of the
217    /// `T` value since the [`Option`] will do it anyway.
218    /// </div>
219    #[inline]
220    pub const fn into_option_copied(self) -> Option<T>
221    where
222        T: Copy,
223    {
224        if self.is_some.to_bool() {
225            Some(self.value)
226        } else {
227            None
228        }
229    }
230
231    /// Returns [`Choice::TRUE`] if the option is the equivalent of a `Some`.
232    #[inline]
233    #[must_use]
234    pub const fn is_some(&self) -> Choice {
235        self.is_some
236    }
237
238    /// Returns [`Choice::TRUE`] if the option is the equivalent of a `None`.
239    #[inline]
240    #[must_use]
241    pub const fn is_none(&self) -> Choice {
242        self.is_some.not()
243    }
244
245    /// Returns `optb` if `self.is_some()` is [`Choice::TRUE`], otherwise returns a [`CtOption`]
246    /// where `self.is_some()` is [`Choice::FALSE`].
247    #[inline]
248    #[must_use]
249    pub fn and<U>(self, mut optb: CtOption<U>) -> CtOption<U> {
250        optb.is_some &= self.is_some;
251        optb
252    }
253
254    /// Calls the provided callback with the wrapped inner value, returning the resulting
255    /// [`CtOption`] value in the event that `self.is_some()` is [`Choice::TRUE`], or if not
256    /// returns a [`CtOption`] with `self.is_none()`.
257    ///
258    /// Unlike [`Option`], the provided callback `f` is unconditionally evaluated to ensure
259    /// constant-time operation. This requires evaluating the function with "dummy" value of `T`
260    /// (e.g. if the [`CtOption`] was constructed with a supplied placeholder value and
261    /// [`Choice::FALSE`], the placeholder value will be provided).
262    #[inline]
263    #[must_use]
264    pub fn and_then<U, F>(self, f: F) -> CtOption<U>
265    where
266        F: FnOnce(T) -> CtOption<U>,
267    {
268        let mut ret = f(self.value);
269        ret.is_some &= self.is_some;
270        ret
271    }
272
273    /// Obtain a reference to the inner value without first checking that `self.is_some()` is
274    /// [`Choice::TRUE`].
275    ///
276    /// This method is primarily intended for use in `const fn` scenarios where it's not yet
277    /// possible to use the safe combinator methods, and returns a reference to avoid issues with
278    /// `const fn` destructors.
279    ///
280    /// <div class="warning">
281    /// <b>Use with care!</b>
282    ///
283    /// This method does not ensure the `value` is actually valid. Callers of this method should
284    /// take great care to ensure that `self.is_some()` is checked elsewhere.
285    /// </div>
286    #[inline]
287    #[must_use]
288    pub const fn as_inner_unchecked(&self) -> &T {
289        &self.value
290    }
291
292    /// Calls the provided callback with the wrapped inner value, which computes a [`Choice`],
293    /// and updates `self.is_some()`.
294    ///
295    /// It updates it to be [`Choice::FALSE`] in the event the returned choice is also false.
296    /// If it was [`Choice::FALSE`] to begin with, it will unconditionally remain that way.
297    #[inline]
298    #[must_use]
299    pub fn filter<P>(mut self, predicate: P) -> Self
300    where
301        P: FnOnce(&T) -> Choice,
302    {
303        self.is_some &= predicate(&self.value);
304        self
305    }
306
307    /// Apply an additional [`Choice`] requirement to `is_some`.
308    #[inline]
309    #[must_use]
310    pub const fn filter_by(mut self, is_some: Choice) -> Self {
311        self.is_some = self.is_some.and(is_some);
312        self
313    }
314
315    /// Maps a `CtOption<T>` to a `CtOption<U>` by unconditionally applying a function to the
316    /// contained `value`, but returning a new option value which inherits `self.is_some()`.
317    #[inline]
318    #[must_use]
319    pub fn map<U, F>(self, f: F) -> CtOption<U>
320    where
321        F: FnOnce(T) -> U,
322    {
323        CtOption::new(f(self.value), self.is_some)
324    }
325
326    /// Maps a `CtOption<T>` to a `U` value, eagerly evaluating the provided function, and returning
327    /// the supplied `default` in the event `self.is_some()` is [`Choice::FALSE`].
328    #[inline]
329    #[must_use = "if you don't need the returned value, use `if let` instead"]
330    pub fn map_or<U, F>(self, default: U, f: F) -> U
331    where
332        U: CtSelect,
333        F: FnOnce(T) -> U,
334    {
335        self.map(f).unwrap_or(default)
336    }
337
338    /// Maps a `CtOption<T>` to a `U` value, eagerly evaluating the provided function, precomputing
339    /// `U::default()` using the [`Default`] trait, and returning it in the event `self.is_some()`
340    /// is [`Choice::FALSE`].
341    #[inline]
342    #[must_use]
343    pub fn map_or_default<U, F>(self, f: F) -> U
344    where
345        U: CtSelect + Default,
346        F: FnOnce(T) -> U,
347    {
348        self.map_or(U::default(), f)
349    }
350
351    /// Transforms a `CtOption<T>` into a `Result<T, E>`, mapping to `Ok(T)` if `self.is_some()` is
352    /// [`Choice::TRUE`], or mapping to the provided `err` in the event `self.is_some()` is
353    /// [`Choice::FALSE`].
354    ///
355    /// <div class="warning">
356    /// <b>Warning: variable-time!</b>
357    ///
358    /// This implementation doesn't intend to be constant-time nor try to protect the leakage of the
359    /// `T` value since the [`Result`] will do it anyway.
360    /// </div>
361    ///
362    /// # Errors
363    /// - Returns `err` in the event `self.is_some()` is [`Choice::FALSE`].
364    #[inline]
365    pub fn ok_or<E>(self, err: E) -> Result<T, E> {
366        self.into_option().ok_or(err)
367    }
368
369    /// Transforms a `CtOption<T>` into a `Result<T, E>` by unconditionally calling the provided
370    /// callback value and using its result in the event `self.is_some()` is [`Choice::FALSE`].
371    ///
372    /// <div class="warning">
373    /// <b>Warning: variable-time!</b>
374    ///
375    /// This implementation doesn't intend to be constant-time nor try to protect the leakage of the
376    /// `T` value since the [`Result`] will do it anyway.
377    /// </div>
378    ///
379    /// # Errors
380    /// - Returns `err` in the event `self.is_some()` is [`Choice::FALSE`].
381    #[inline]
382    #[allow(clippy::missing_errors_doc)]
383    pub fn ok_or_else<E, F>(self, err: F) -> Result<T, E>
384    where
385        F: FnOnce() -> E,
386    {
387        self.ok_or(err())
388    }
389
390    /// Returns `self` if `self.is_some()` is [`Choice::TRUE`], otherwise returns `optb`.
391    #[inline]
392    #[must_use]
393    pub fn or(self, optb: CtOption<T>) -> CtOption<T>
394    where
395        T: CtSelect,
396    {
397        CtOption {
398            value: self.value.ct_select(&optb.value, self.is_none()),
399            is_some: self.is_some | optb.is_some,
400        }
401    }
402
403    /// Obtain a copy of the inner value without first checking that `self.is_some()` is
404    /// [`Choice::TRUE`].
405    ///
406    /// This method is primarily intended for use in `const fn` scenarios where it's not yet
407    /// possible to use the safe combinator methods, and uses a `Copy` bound to avoid issues with
408    /// `const fn` destructors.
409    ///
410    /// <div class="warning">
411    /// <b>Use with care!</b>
412    ///
413    /// This method does not ensure the `value` is actually valid. Callers of this method should
414    /// take great care to ensure that `self.is_some()` is checked elsewhere.
415    /// </div>
416    #[inline]
417    #[must_use]
418    pub const fn to_inner_unchecked(self) -> T
419    where
420        T: Copy,
421    {
422        self.value
423    }
424
425    /// Return the contained value, consuming the `self` value.
426    ///
427    /// Use of this function is discouraged due to panic potential. Instead, prefer non-panicking
428    /// alternatives such as `unwrap_or` or `unwrap_or_default` which operate in constant-time.
429    ///
430    /// As the final step of a sequence of constant-time operations, or in the event you are dealing
431    /// with a [`CtOption`] in a non-secret context where constant-time does not matter, you can
432    /// also convert to [`Option`] using `into_option` or the [`From`] impl on [`Option`]. Note
433    /// this introduces a branch and with it a small amount of timing variability. If possible try
434    /// to avoid this branch when writing constant-time code (e.g. use implicit rejection instead
435    /// of `Option`/`Result` to handle errors)
436    ///
437    /// # Panics
438    /// In the event `self.is_some()` is [`Choice::FALSE`].
439    #[inline]
440    #[must_use]
441    #[track_caller]
442    pub fn unwrap(self) -> T {
443        assert!(
444            self.is_some.to_bool(),
445            "called `CtOption::unwrap()` on a value with `is_some` set to `Choice::FALSE`"
446        );
447        self.value
448    }
449
450    /// Return the contained value in the event `self.is_some()` is [`Choice::TRUE`], or if not,
451    /// uses a provided default.
452    #[inline]
453    #[must_use]
454    pub fn unwrap_or(self, default: T) -> T
455    where
456        T: CtSelect,
457    {
458        default.ct_select(&self.value, self.is_some)
459    }
460
461    /// Unconditionally computes `T::default()` using the [`Default`] trait, then returns either
462    /// the contained value if `self.is_some()` is [`Choice::TRUE`], or if it's [`Choice::FALSE`]
463    /// returns the previously computed default.
464    #[inline]
465    #[must_use]
466    pub fn unwrap_or_default(self) -> T
467    where
468        T: CtSelect + Default,
469    {
470        self.unwrap_or(T::default())
471    }
472
473    /// Returns an "is some" [`CtOption`] with the contained value from either `self` or `optb` in
474    /// the event exactly one of them has `self.is_some()` set to [`Choice::TRUE`], or else returns
475    /// a [`CtOption`] with `self.is_some()` set to [`Choice::FALSE`].
476    #[inline]
477    #[must_use]
478    pub fn xor(self, optb: CtOption<T>) -> CtOption<T>
479    where
480        T: CtSelect,
481    {
482        CtOption {
483            value: self.value.ct_select(&optb.value, self.is_none()),
484            is_some: self.is_some ^ optb.is_some,
485        }
486    }
487
488    /// Zips `self` with another [`CtOption`].
489    ///
490    /// If `self.is_some() && other.is_some()`, this method returns a new [`CtOption`] for a 2-tuple
491    /// of their contents where `is_some()` is [`Choice::TRUE`].
492    ///
493    /// Otherwise, a [`CtOption`] where `is_some()` is [`Choice::FALSE`] is returned.
494    pub fn zip<U>(self, other: CtOption<U>) -> CtOption<(T, U)> {
495        CtOption {
496            value: (self.value, other.value),
497            is_some: self.is_some & other.is_some,
498        }
499    }
500
501    /// Zips `self` and another `CtOption` with function `f`.
502    ///
503    /// If `self.is_some() && other.is_some()`, this method returns a new [`CtOption`] for
504    /// the result of `f` applied to their inner values where `is_some()` is [`Choice::TRUE`].
505    ///
506    /// Otherwise, a [`CtOption`] where `is_some()` is [`Choice::FALSE`] is returned.
507    pub fn zip_with<U, F, R>(self, other: CtOption<U>, f: F) -> CtOption<R>
508    where
509        F: FnOnce(T, U) -> R,
510    {
511        self.zip(other).map(|(a, b)| f(a, b))
512    }
513}
514
515impl<T> CtOption<&T> {
516    /// Maps a `CtOption<&T>` to `CtOption<T>` by copying the contents of the option.
517    #[must_use = "`self` will be dropped if the result is not used"]
518    pub const fn copied(self) -> CtOption<T>
519    where
520        T: Copy,
521    {
522        CtOption {
523            value: *self.value,
524            is_some: self.is_some,
525        }
526    }
527
528    /// Maps a `CtOption<&T>` to `CtOption<T>` by cloning the contents of the option.
529    #[must_use = "`self` will be dropped if the result is not used"]
530    pub fn cloned(self) -> CtOption<T>
531    where
532        T: Clone,
533    {
534        CtOption {
535            value: self.value.clone(),
536            is_some: self.is_some,
537        }
538    }
539}
540
541impl<T> CtOption<&mut T> {
542    /// Maps a `CtOption<&mut T>` to `CtOption<T>` by copying the contents of the option.
543    #[must_use = "`self` will be dropped if the result is not used"]
544    pub const fn copied(self) -> CtOption<T>
545    where
546        T: Copy,
547    {
548        CtOption {
549            value: *self.value,
550            is_some: self.is_some,
551        }
552    }
553
554    /// Maps a `CtOption<&mut T>` to `CtOption<T>` by cloning the contents of the option.
555    #[must_use = "`self` will be dropped if the result is not used"]
556    pub fn cloned(self) -> CtOption<T>
557    where
558        T: Clone,
559    {
560        CtOption {
561            value: self.value.clone(),
562            is_some: self.is_some,
563        }
564    }
565}
566
567impl<T: CtAssign> CtAssign for CtOption<T> {
568    fn ct_assign(&mut self, other: &Self, choice: Choice) {
569        self.value.ct_assign(&other.value, choice);
570        self.is_some.ct_assign(&other.is_some, choice);
571    }
572}
573impl<T: CtAssign> CtAssignSlice for CtOption<T> {}
574
575impl<T: CtEq> CtEq for CtOption<T> {
576    #[inline]
577    fn ct_eq(&self, other: &CtOption<T>) -> Choice {
578        (self.is_some & other.is_some & self.value.ct_eq(&other.value))
579            | (self.is_none() & other.is_none())
580    }
581}
582
583impl<T: CtEq> CtEqSlice for CtOption<T> {}
584
585impl<T: CtSelect> CtSelect for CtOption<T> {
586    fn ct_select(&self, other: &Self, choice: Choice) -> Self {
587        Self {
588            value: self.value.ct_select(&other.value, choice),
589            is_some: self.is_some.ct_select(&other.is_some, choice),
590        }
591    }
592}
593
594impl<T: Default> Default for CtOption<T> {
595    fn default() -> Self {
596        Self::none()
597    }
598}
599
600/// Convert the [`CtOption`] wrapper into an [`Option`], depending on whether
601/// [`CtOption::is_some`] is a truthy or falsy [`Choice`].
602///
603/// <div class="warning">
604/// <b>Warning: variable-time!</b>
605///
606/// This implementation doesn't intend to be constant-time nor try to protect the leakage of the
607/// `T` value since the `Option` will do it anyway.
608/// </div>
609impl<T> From<CtOption<T>> for Option<T> {
610    fn from(src: CtOption<T>) -> Option<T> {
611        src.into_option()
612    }
613}
614
615/// NOTE: in order to be able to unwrap the `subtle::CtOption` we rely on a `Default` bound in
616/// order to have a placeholder value, and `ConditionallySelectable` to be able to use `unwrap_or`.
617#[cfg(feature = "subtle")]
618impl<T> From<subtle::CtOption<T>> for CtOption<T>
619where
620    T: subtle::ConditionallySelectable + Default,
621{
622    #[inline]
623    fn from(src: subtle::CtOption<T>) -> CtOption<T> {
624        let is_some = src.is_some();
625        CtOption {
626            value: src.unwrap_or(Default::default()),
627            is_some: is_some.into(),
628        }
629    }
630}
631
632#[cfg(feature = "subtle")]
633impl<T> From<CtOption<T>> for subtle::CtOption<T> {
634    #[inline]
635    fn from(src: CtOption<T>) -> subtle::CtOption<T> {
636        subtle::CtOption::new(src.value, src.is_some.into())
637    }
638}
639
640#[cfg(feature = "subtle")]
641impl<T> subtle::ConditionallySelectable for CtOption<T>
642where
643    T: Copy, // `ConditionallySelectable` supertrait bound
644    Self: CtSelect,
645{
646    #[inline]
647    fn conditional_select(a: &Self, b: &Self, choice: subtle::Choice) -> Self {
648        CtSelect::ct_select(a, b, choice.into())
649    }
650}
651
652#[cfg(feature = "subtle")]
653impl<T> subtle::ConstantTimeEq for CtOption<T>
654where
655    Self: CtEq,
656{
657    #[inline]
658    fn ct_eq(&self, other: &Self) -> subtle::Choice {
659        CtEq::ct_eq(self, other).into()
660    }
661}
662
663#[cfg(test)]
664mod tests {
665    use crate::{Choice, CtEq, CtOption, CtSelect};
666
667    /// Example wrapped value for testing
668    const VALUE: u8 = 42;
669
670    /// Example option which is like `Option::Some`
671    const SOME: CtOption<u8> = CtOption::new(VALUE, Choice::TRUE);
672
673    /// Example option which is like `Option::None`
674    const NONE: CtOption<u8> = CtOption::new(VALUE, Choice::FALSE);
675
676    /// Another option containing a different value
677    const OTHER: CtOption<u8> = CtOption::new(VALUE + 1, Choice::TRUE);
678
679    /// Dummy error type
680    #[derive(Debug, Eq, PartialEq)]
681    struct Error;
682
683    #[test]
684    fn map_macro() {
685        assert!(map!(NONE, u16::from).is_none().to_bool());
686        assert_eq!(map!(SOME, u16::from).unwrap(), u16::from(VALUE));
687    }
688
689    #[test]
690    fn unwrap_or_macro() {
691        // Don't actually use this! It's just a test function implemented in variable-time
692        #[allow(clippy::trivially_copy_pass_by_ref)]
693        const fn select_vartime(a: &u8, b: &u8, choice: Choice) -> u8 {
694            if choice.to_bool() { *b } else { *a }
695        }
696
697        assert_eq!(
698            unwrap_or!(NONE, OTHER.unwrap(), select_vartime),
699            OTHER.unwrap()
700        );
701        assert_eq!(unwrap_or!(SOME, OTHER.unwrap(), select_vartime), VALUE);
702    }
703
704    #[test]
705    fn ct_eq() {
706        assert!(NONE.ct_eq(&NONE).to_bool());
707        assert!(NONE.ct_ne(&SOME).to_bool());
708        assert!(SOME.ct_ne(&NONE).to_bool());
709        assert!(SOME.ct_eq(&SOME).to_bool());
710        assert!(SOME.ct_ne(&OTHER).to_bool());
711    }
712
713    #[test]
714    fn ct_select() {
715        assert!(NONE.ct_select(&SOME, Choice::FALSE).is_none().to_bool());
716        assert!(NONE.ct_select(&SOME, Choice::TRUE).ct_eq(&SOME).to_bool());
717        assert!(SOME.ct_select(&NONE, Choice::FALSE).ct_eq(&SOME).to_bool());
718        assert!(SOME.ct_select(&NONE, Choice::TRUE).is_none().to_bool());
719    }
720
721    #[test]
722    fn default() {
723        assert!(NONE.ct_eq(&CtOption::default()).to_bool());
724    }
725
726    #[test]
727    fn expect_some() {
728        assert_eq!(SOME.expect("should succeed"), VALUE);
729    }
730
731    #[test]
732    #[should_panic]
733    fn expect_none() {
734        let _ = NONE.expect("should panic");
735    }
736
737    #[test]
738    fn into_option() {
739        assert_eq!(SOME.into_option(), Some(VALUE));
740        assert_eq!(NONE.into_option(), None);
741    }
742
743    #[test]
744    fn into_option_copied() {
745        assert_eq!(SOME.into_option_copied(), Some(VALUE));
746        assert_eq!(NONE.into_option_copied(), None);
747    }
748
749    #[test]
750    fn is_some() {
751        assert!(SOME.is_some().to_bool());
752        assert!(!NONE.is_some().to_bool());
753    }
754
755    #[test]
756    fn is_none() {
757        assert!(!SOME.is_none().to_bool());
758        assert!(NONE.is_none().to_bool());
759    }
760
761    #[test]
762    fn and() {
763        assert!(SOME.and(NONE).is_none().to_bool());
764        assert_eq!(SOME.and(OTHER).unwrap(), OTHER.unwrap());
765    }
766
767    #[test]
768    fn and_then() {
769        assert!(NONE.and_then(|_| NONE).is_none().to_bool());
770        assert!(NONE.and_then(|_| SOME).is_none().to_bool());
771
772        let ret = SOME.and_then(|value| {
773            assert_eq!(VALUE, value);
774            OTHER
775        });
776        assert!(ret.ct_eq(&OTHER).to_bool());
777    }
778
779    #[test]
780    fn filter() {
781        assert!(NONE.filter(|_| Choice::TRUE).ct_eq(&NONE).to_bool());
782        assert!(NONE.filter(|_| Choice::FALSE).ct_eq(&NONE).to_bool());
783        assert!(SOME.filter(|_| Choice::FALSE).ct_eq(&NONE).to_bool());
784
785        let ret = SOME.filter(|&value| {
786            assert_eq!(VALUE, value);
787            Choice::TRUE
788        });
789        assert_eq!(ret.unwrap(), VALUE);
790    }
791
792    #[test]
793    fn filter_by() {
794        assert!(NONE.filter_by(Choice::FALSE).is_none().to_bool());
795        assert!(NONE.filter_by(Choice::TRUE).is_none().to_bool());
796        assert!(SOME.filter_by(Choice::FALSE).ct_eq(&NONE).to_bool());
797        assert_eq!(SOME.filter_by(Choice::TRUE).unwrap(), VALUE);
798    }
799
800    #[test]
801    fn insert() {
802        let mut example = NONE;
803        assert!(example.is_none().to_bool());
804
805        let ret = example.insert(42);
806        assert_eq!(ret, &42);
807        assert!(example.is_some().to_bool());
808    }
809
810    #[test]
811    fn insert_if() {
812        let mut example = NONE;
813        assert!(example.is_none().to_bool());
814
815        example.insert_if(&42, Choice::FALSE);
816        assert!(example.is_none().to_bool());
817
818        example.insert_if(&42, Choice::TRUE);
819        assert_eq!(example.unwrap(), 42);
820    }
821
822    #[test]
823    fn map() {
824        assert!(NONE.map(|value| value + 1).ct_eq(&NONE).to_bool());
825        assert!(SOME.map(|value| value + 1).ct_eq(&OTHER).to_bool());
826    }
827
828    #[test]
829    fn map_or() {
830        let example = 52;
831        assert_eq!(NONE.map_or(example, |value| value + 1), example);
832        assert_eq!(SOME.map_or(example, |value| value + 1), VALUE + 1);
833    }
834
835    #[test]
836    fn map_or_default() {
837        assert_eq!(NONE.map_or_default(|value| value + 1), Default::default());
838        assert_eq!(SOME.map_or_default(|value| value + 1), VALUE + 1);
839    }
840
841    #[test]
842    fn ok_or() {
843        assert_eq!(NONE.ok_or(Error), Err(Error));
844        assert_eq!(SOME.ok_or(Error), Ok(VALUE));
845    }
846
847    #[test]
848    fn ok_or_else() {
849        assert_eq!(NONE.ok_or_else(|| Error), Err(Error));
850        assert_eq!(SOME.ok_or_else(|| Error), Ok(VALUE));
851    }
852
853    #[test]
854    fn or() {
855        assert!(NONE.or(NONE).is_none().to_bool());
856        assert!(SOME.or(NONE).ct_eq(&SOME).to_bool());
857        assert!(NONE.or(SOME).ct_eq(&SOME).to_bool());
858        assert!(SOME.or(OTHER).ct_eq(&SOME).to_bool());
859    }
860
861    #[test]
862    fn some() {
863        assert!(CtOption::some(VALUE).ct_eq(&SOME).to_bool());
864    }
865
866    #[test]
867    fn unwrap_some() {
868        assert_eq!(SOME.unwrap(), VALUE);
869    }
870
871    #[test]
872    #[should_panic]
873    fn unwrap_none() {
874        let _ = NONE.unwrap();
875    }
876
877    #[test]
878    fn unwrap_or() {
879        let example = 52;
880        assert_eq!(NONE.unwrap_or(example), example);
881        assert_eq!(SOME.unwrap_or(example), VALUE);
882    }
883
884    #[test]
885    fn unwrap_or_default() {
886        assert_eq!(NONE.unwrap_or_default(), Default::default());
887        assert_eq!(SOME.unwrap_or_default(), VALUE);
888    }
889
890    #[test]
891    fn xor() {
892        assert!(NONE.xor(NONE).is_none().to_bool());
893        assert!(SOME.xor(NONE).ct_eq(&SOME).to_bool());
894        assert!(NONE.xor(SOME).ct_eq(&SOME).to_bool());
895        assert!(SOME.xor(OTHER).is_none().to_bool());
896    }
897
898    #[test]
899    fn zip() {
900        assert!(NONE.zip(NONE).is_none().to_bool());
901        assert!(NONE.zip(SOME).is_none().to_bool());
902        assert!(SOME.zip(NONE).is_none().to_bool());
903        assert_eq!(SOME.zip(OTHER).unwrap(), (SOME.unwrap(), OTHER.unwrap()));
904    }
905
906    #[test]
907    fn zip_with() {
908        assert!(NONE.zip_with(NONE, |a, b| a + b).is_none().to_bool());
909        assert!(NONE.zip_with(SOME, |a, b| a + b).is_none().to_bool());
910        assert!(SOME.zip_with(NONE, |a, b| a + b).is_none().to_bool());
911        assert_eq!(
912            SOME.zip_with(OTHER, |a, b| a + b).unwrap(),
913            SOME.unwrap() + OTHER.unwrap()
914        );
915    }
916}