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