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}