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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(all(feature = "nightly", feature = "std"), feature(try_trait))]
#![warn(missing_docs)]
#![deny(missing_debug_implementations)]
#![cfg_attr(test, deny(warnings))]

//! <style>
//! /* Provides formatting for the feature list */
//! .desc {
//!     padding-left: 1em;
//!     margin-bottom: 1em;
//!     display: block;
//! }
//! .icon {
//!   width: 1em;
//!   height: 1em;
//!   display: inline-flex;
//!   top: .125em;
//!   position: relative;
//! }
//! .check-mark {
//!   background: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='3.8 5 16.8 16.8' width='100%' height='100%'><path d='M9 16.2l-3.5-3.5c-.39-.39-1.01-.39-1.4 0-.39.39-.39 1.01 0 1.4l4.19 4.19c.39.39 1.02.39 1.41 0L20.3 7.7c.39-.39.39-1.01 0-1.4-.39-.39-1.01-.39-1.4 0L9 16.2' fill='green' /></svg>") no-repeat;
//! }
//! .cross-mark {
//!   background: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='3.5 3.5 16.8 16.8' width='100%' height='100%'><path d='M18.3 5.71c-.39-.39-1.02-.39-1.41 0L12 10.59 7.11 5.7c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41L10.59 12 5.7 16.89c-.39.39-.39 1.02 0 1.41.39.39 1.02.39 1.41 0L12 13.41l4.89 4.89c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L13.41 12l4.89-4.89c.38-.38.38-1.02 0-1.4z' fill='red' /></svg>");
//! }
//! </style>
//!
//! An approximation of Kotlin's chaining functions like [let](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/let.html) and [also](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/also.html)
//! # Features
//! - nightly
#![cfg_attr(feature = "nightly", doc = r#"<i class="check-mark icon" /></i>"#)]
#![cfg_attr(not(feature = "nightly"), doc = r#"<i class="cross-mark icon" /></i>"#)]
//! <span class="desc">Additional features requiring the nightly compiler</span>
//! - std
#![cfg_attr(feature = "std", doc = r#"<i class="check-mark icon" /></i>"#)]
#![cfg_attr(not(feature = "std"), doc = r#"<i class="cross-mark icon" /></i>"#)]
//! <span class="desc">Additional features requiring the Rust Standard Library</span>

#[cfg(all(feature = "nightly", feature = "std"))]
use std::ops::Try;

/// Provides Kotlin-esque helper functions for all types via a blanket impl, enabling easier function chaining patterns
/// ```
/// # use also::*;
/// let not_empty = ().lets(|_| "Hello, world!");
/// assert_eq!("Hello, world!", not_empty);
/// ```
pub trait Also {
    /// Calls a function with the receiver, and returns the result.
    /// Akin to Kotlin's [let](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/let.html)
    /// extension function
    /// # Examples
    /// ```
    /// # use also::*;
    /// let x = "Hello, world!".lets(|x| x.len());
    /// assert_eq!(13, x);
    ///
    /// let x = "Hello, world!".to_string().lets(|mut x| {
    ///     x.push('g');
    ///     x.len()
    /// });
    /// assert_eq!(14, x);
    /// ```
    #[inline(always)]
    fn lets<R>(self, f: impl FnOnce(Self) -> R) -> R
    where
        Self: Sized,
    {
        f(self)
    }

    /// Returns the receiver if the given function returns `Ok`, else forwards the `Err`.
    /// Akin to Kotlin's [takeIf](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/take-if.html)
    /// extension function
    /// # Examples
    /// ```
    /// # use also::*;
    /// let x = "42".take_if(|x| u8::from_str_radix(x, 10));
    /// assert_eq!(Ok("42"), x);
    ///
    /// let x = "aa".take_if(|x| u8::from_str_radix(x, 10));
    /// assert!(x.is_err());
    /// ```
    #[inline(always)]
    fn take_if<R, E>(mut self, f: impl FnOnce(&mut Self) -> Result<R, E>) -> Result<Self, E>
    where
        Self: Sized,
    {
        f(&mut self).map(|_| self)
    }

    /// Calls a function with the receiver, and returns the receiver.
    /// Akin to Kotlin's [also](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/also.html)
    /// extension function
    /// # Examples
    /// ```
    /// # use also::*;
    /// let x = "Hello".to_string().also(|x| x.push_str(", world!"));
    /// assert_eq!("Hello, world!", x);
    /// ```
    #[inline(always)]
    fn also<R>(mut self, f: impl FnOnce(&mut Self) -> R) -> Self
    where
        Self: Sized,
    {
        f(&mut self);
        self
    }

    /// Calls a function with the `Ok` contained value and returns the `Result`.
    /// # Examples
    /// ```
    /// # use also::*;
    /// let x: Result<String, ()> = Ok("Hello".to_string()).and_run(|s| s.push('!'));
    /// assert_eq!(Ok("Hello!".to_string()), x);
    /// ```
    #[inline(always)]
    #[cfg(all(feature = "nightly", feature = "std"))]
    fn and_run<R, E, T>(self, f: impl FnOnce(&mut R) -> T) -> Self
    where
        Self: Try<Ok = R, Error = E> + Sized,
    {
        match Try::into_result(self) {
            Ok(mut r) => {
                f(&mut r);
                Try::from_ok(r)
            }
            Err(e) => Try::from_error(e),
        }
    }

    /// Calls a function with the `Err` contained value and returns the `Result`.
    /// # Examples
    /// ```
    /// # use also::*;
    /// let x: Result<(), String> = Err("Hello".to_string()).or_run(|s| s.push('!'));
    /// assert_eq!(Err("Hello!".to_string()), x);
    /// ```
    #[inline(always)]
    #[cfg(all(feature = "nightly", feature = "std"))]
    fn or_run<R, E, T>(self, f: impl FnOnce(&mut E) -> T) -> Self
    where
        Self: Try<Ok = R, Error = E> + Sized,
    {
        match Try::into_result(self) {
            Ok(r) => Try::from_ok(r),
            Err(mut e) => {
                f(&mut e);
                Try::from_error(e)
            }
        }
    }
}

impl<T> Also for T {}