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}