mz_ore/option.rs
1// Copyright 2019 The Rust Project Contributors
2// Copyright Materialize, Inc. and contributors. All rights reserved.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License in the LICENSE file at the
7// root of this repository, or online at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16//
17// Portions of this file are derived from the Option implementation in the
18// libcore crate distributed as part of the Rust project. The original source
19// code was retrieved on April 18, 2019 from:
20//
21// https://github.com/rust-lang/rust/blob/e928e9441157f63a776ba1f8773818838e0912ea/src/libcore/option.rs
22//
23// The original source code is subject to the terms of the MIT license, a copy
24// of which can be found in the LICENSE file at the root of this repository.
25
26//! Option utilities.
27
28use std::fmt;
29use std::ops::Deref;
30
31use either::Either;
32
33/// Extension methods for [`std::option::Option`].
34pub trait OptionExt<T> {
35 /// Converts from `Option<&T>` to `Option<T::Owned>` when `T` implements
36 /// [`ToOwned`].
37 ///
38 /// The canonical use case is converting from an `Option<&str>` to an
39 /// `Option<String>`.
40 ///
41 /// The name is symmetric with [`Option::cloned`].
42 fn owned(&self) -> Option<<<T as Deref>::Target as ToOwned>::Owned>
43 where
44 T: Deref,
45 T::Target: ToOwned;
46
47 /// Returns a type that displays the option's value if it is present, or
48 /// the provided default otherwise.
49 ///
50 /// # Examples
51 ///
52 /// ```
53 /// use mz_ore::option::OptionExt;
54 ///
55 /// fn render(number: Option<i32>) -> String {
56 /// format!("Your lucky number is {}.", number.display_or("unknown"))
57 /// }
58 ///
59 /// assert_eq!(render(Some(42)), "Your lucky number is 42.");
60 /// assert_eq!(render(None), "Your lucky number is unknown.");
61 /// ```
62 fn display_or<D>(self, default: D) -> Either<T, D>
63 where
64 T: fmt::Display,
65 D: fmt::Display;
66
67 /// Like [`OptionExt::display_or`], but the default value is computed
68 /// only if the option is `None`.
69 ///
70 /// # Examples
71 ///
72 /// ```
73 /// use mz_ore::option::OptionExt;
74 ///
75 /// fn render(number: Option<i32>, guess: i32) -> String {
76 /// format!(
77 /// "Your lucky number is {}.",
78 /// number.display_or_else(|| format!("unknown (best guess: {})", guess)),
79 /// )
80 /// }
81 ///
82 /// assert_eq!(render(Some(42), 7), "Your lucky number is 42.");
83 /// assert_eq!(render(None, 7), "Your lucky number is unknown (best guess: 7).");
84 /// ```
85 fn display_or_else<D, R>(self, default: D) -> Either<T, R>
86 where
87 T: fmt::Display,
88 D: FnOnce() -> R,
89 R: fmt::Display;
90}
91
92impl<T> OptionExt<T> for Option<T> {
93 fn owned(&self) -> Option<<<T as Deref>::Target as ToOwned>::Owned>
94 where
95 T: Deref,
96 T::Target: ToOwned,
97 {
98 self.as_ref().map(|x| x.deref().to_owned())
99 }
100
101 fn display_or<D>(self, default: D) -> Either<T, D>
102 where
103 T: fmt::Display,
104 D: fmt::Display,
105 {
106 match self {
107 Some(t) => Either::Left(t),
108 None => Either::Right(default),
109 }
110 }
111
112 fn display_or_else<D, R>(self, default: D) -> Either<T, R>
113 where
114 T: fmt::Display,
115 D: FnOnce() -> R,
116 R: fmt::Display,
117 {
118 match self {
119 Some(t) => Either::Left(t),
120 None => Either::Right(default()),
121 }
122 }
123}
124
125/// From <https://github.com/rust-lang/rust/issues/38282#issuecomment-266275785>
126///
127/// Extend `Option` with a fallible map method.
128///
129/// (Note that the usual collect trick can't be used with Option, unless I'm missing something.)
130///
131/// # Type parameters
132///
133/// - `T`: The input `Option`'s value type
134/// - `U`: The outputs `Option`'s value type
135/// - `E`: The possible error during the mapping
136pub trait FallibleMapExt<T, U, E> {
137 /// Try to apply a fallible map function to the option
138 fn try_map<F>(&self, f: F) -> Result<Option<U>, E>
139 where
140 F: FnOnce(&T) -> Result<U, E>;
141}
142
143impl<T, U, E> FallibleMapExt<T, U, E> for Option<T> {
144 fn try_map<F>(&self, f: F) -> Result<Option<U>, E>
145 where
146 F: FnOnce(&T) -> Result<U, E>,
147 {
148 match self {
149 &Some(ref x) => f(x).map(Some),
150 &None => Ok(None),
151 }
152 }
153}