1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// Copyright 2019 The Rust Project Contributors
// Copyright Materialize, Inc. and contributors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License in the LICENSE file at the
// root of this repository, or online at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Portions of this file are derived from the Option implementation in the
// libcore crate distributed as part of the Rust project. The original source
// code was retrieved on April 18, 2019 from:
//
//     https://github.com/rust-lang/rust/blob/e928e9441157f63a776ba1f8773818838e0912ea/src/libcore/option.rs
//
// The original source code is subject to the terms of the MIT license, a copy
// of which can be found in the LICENSE file at the root of this repository.

//! Option utilities.

use std::fmt;
use std::ops::Deref;

use either::Either;

/// Extension methods for [`std::option::Option`].
pub trait OptionExt<T> {
    /// Converts from `Option<&T>` to `Option<T::Owned>` when `T` implements
    /// [`ToOwned`].
    ///
    /// The canonical use case is converting from an `Option<&str>` to an
    /// `Option<String>`.
    ///
    /// The name is symmetric with [`Option::cloned`].
    fn owned(&self) -> Option<<<T as Deref>::Target as ToOwned>::Owned>
    where
        T: Deref,
        T::Target: ToOwned;

    /// Returns a type that displays the option's value if it is present, or
    /// the provided default otherwise.
    ///
    /// # Examples
    ///
    /// ```
    /// use ore::option::OptionExt;
    ///
    /// fn render(number: Option<i32>) -> String {
    ///     format!("Your lucky number is {}.", number.display_or("unknown"))
    /// }
    ///
    /// assert_eq!(render(Some(42)), "Your lucky number is 42.");
    /// assert_eq!(render(None), "Your lucky number is unknown.");
    /// ```
    fn display_or<D>(self, default: D) -> Either<T, D>
    where
        T: fmt::Display,
        D: fmt::Display;

    /// Like [`OptionExt::display_or`], but the default value is computed
    /// only if the option is `None`.
    ///
    /// # Examples
    ///
    /// ```
    /// use ore::option::OptionExt;
    ///
    /// fn render(number: Option<i32>, guess: i32) -> String {
    ///     format!(
    ///         "Your lucky number is {}.",
    ///         number.display_or_else(|| format!("unknown (best guess: {})", guess)),
    ///     )
    /// }
    ///
    /// assert_eq!(render(Some(42), 7), "Your lucky number is 42.");
    /// assert_eq!(render(None, 7), "Your lucky number is unknown (best guess: 7).");
    /// ```
    fn display_or_else<D, R>(self, default: D) -> Either<T, R>
    where
        T: fmt::Display,
        D: FnOnce() -> R,
        R: fmt::Display;
}

impl<T> OptionExt<T> for Option<T> {
    fn owned(&self) -> Option<<<T as Deref>::Target as ToOwned>::Owned>
    where
        T: Deref,
        T::Target: ToOwned,
    {
        self.as_ref().map(|x| x.deref().to_owned())
    }

    fn display_or<D>(self, default: D) -> Either<T, D>
    where
        T: fmt::Display,
        D: fmt::Display,
    {
        match self {
            Some(t) => Either::Left(t),
            None => Either::Right(default),
        }
    }

    fn display_or_else<D, R>(self, default: D) -> Either<T, R>
    where
        T: fmt::Display,
        D: FnOnce() -> R,
        R: fmt::Display,
    {
        match self {
            Some(t) => Either::Left(t),
            None => Either::Right(default()),
        }
    }
}