into-owned 0.2.0

Provides a trait for associating a type with its owned variant
Documentation
// Copyright 2020 Koichi Kitahara
//
// Licensed under either of Apache License, Version 2.0:
//
//   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 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.
//
// or MIT license:
//
//   Permission is hereby granted, free of charge, to any person obtaining a
//   copy of this software and associated documentation files (the "Software"),
//   to deal in the Software without restriction, including without limitation
//   the rights to use, copy, modify, merge, publish, distribute, sublicense,
//   and/or sell copies of the Software, and to permit persons to whom the
//   Software is furnished to do so, subject to the following conditions:
//
//   The above copyright notice and this permission notice shall be included in
//   all copies or substantial portions of the Software.
//
//   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
//   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
//   DEALINGS IN THE SOFTWARE.
//
// at your option.

#![cfg_attr(not(feature = "std"), no_std)]
#![doc(html_root_url = "https://docs.rs/into-owned/0.2.0")]

//! This crate provides a trait [`IntoOwned`] for associating a type with its owned variant.
//!
//! [`IntoOwned`]: trait.IntoOwned.html
//!
//!  In this experimental version, [`IntoOwned`] is implemented only for primitive numeric types
//!  (`i8`, `i16`, `i32`, `i64`, `i128`, `isize`, `u8`, `u16`, `u32`, `u64`, `u128`, `usize`, `f32`, and `f64`)
//!  and their references.

// pub mod gyu;

use core::borrow::Borrow;

/// A trait for associating a type with its owned variant.
///
/// # Examples
///
/// Implementing the `IntoOwned` trait for an owned type is straighitforward.
/// Maybe a derive macro is supplied in future.
/// ```
/// use into_owned::{IntoOwned, Is};
///
/// #[derive(Clone)]
/// struct A();
///
/// impl IntoOwned for A {
///     type Owned = Self;
///
///     fn is_owned(&self) -> bool {
///         true
///     }
///
///     fn into_owned(self) -> Self {
///         self
///     }
///
///     fn try_as_mut(&mut self) -> Option<&mut Self> {
///         Some(self)
///     }
///
///     fn as_is<'a>(self) -> Is<'a, Self> {
///         Is::Owned(self)
///     }
/// }
/// ```
/// Since there are blanket implementations for `&T` and `&mut T` where
/// `T: IntoOwned<Owned = T> + Clone`,
/// manually implementing `IntoOwned` for the owned type is sufficient in most cases.
/// ```
/// # use into_owned::{IntoOwned, Is};
/// #
/// # #[derive(Clone)]
/// # struct A();
/// #
/// # impl IntoOwned for A {
/// #     type Owned = Self;
/// #
/// #     fn is_owned(&self) -> bool {
/// #        true
/// #     }
/// #
/// #     fn into_owned(self) -> Self {
/// #         self
/// #     }
/// #
/// #     fn try_as_mut(&mut self) -> Option<&mut Self> {
/// #         Some(self)
/// #     }
/// #
/// #     fn as_is<'a>(self) -> Is<'a, Self> {
/// #         Is::Owned(self)
/// #     }
/// # }
/// // this function can be called with an owned value or a borrowed reference
/// fn is_owned<T>(t: T) -> bool
/// where T: IntoOwned
/// {
///     t.is_owned()
/// }
///
/// assert_eq!(is_owned(A()), true);
/// assert_eq!(is_owned(&A()), false);
/// assert_eq!(is_owned(&mut A()), false);
/// ```
pub trait IntoOwned
where
    Self: Sized + Borrow<<Self as IntoOwned>::Owned>,
{
    /// The owned type associated with `Self`.
    type Owned;

    /// Returns `true` if `self` is an owned value
    /// (e.g. if `Self` and `Self::Owned` are the same),
    /// or `false` otherwise.
    ///
    /// This method is used as a hint of whether [`into_owned`] method is cheap or not.
    ///
    /// [`into_owned`]: #method.into_owned
    fn is_owned(&self) -> bool;

    /// Converts `self` into an owned value.
    ///
    /// If `Self` and `Self::Owned` are the same, usually it just returns `self`.
    /// If not, usually it returns an owned value by cloning.
    fn into_owned(self) -> Self::Owned;

    /// Returns a mutable reference to the owned value of `self` if possible.
    /// This method should be cheap.
    ///
    /// If a mutable reference is required and taking the reference must be cheap,
    /// `BorrowMut` trait should be used instead.
    fn try_as_mut(&mut self) -> Option<&mut Self::Owned>;

    /// Returns `self` as [`Is<'a, Self>`].
    ///
    /// This method is useful when `self` is used differently depending on
    /// its state: owned, (immutably) borrowed, or mutably borrowed.
    ///
    /// # Examples
    /// ```
    /// use into_owned::{IntoOwned, Is};
    ///
    /// // returns:
    /// // * `0` if `t` is an owned value
    /// // * `1` if `t` is an immutably borrowed reference
    /// // * `2` if `t` is a mutably borrowed reference
    /// fn a<T>(t: T) -> i8
    /// where T: IntoOwned
    /// {
    ///     match t.as_is() {
    ///         Is::Owned(x) => {
    ///             // here `x: T::Owned`
    ///             0
    ///         }
    ///         Is::Borrowed(x) => {
    ///             // here `x: &T::Owned`
    ///             1
    ///         }
    ///         Is::MutBorrowed(x) => {
    ///             // here `x: &mut T::Owned`
    ///             2
    ///         }
    ///     }
    /// }
    ///
    /// assert_eq!(a(1.0), 0);
    /// assert_eq!(a(&1.0), 1);
    /// assert_eq!(a(&mut 1.0), 2);
    /// ```
    /// [`Is<'a, Self>`]: enum.Is.html
    fn as_is<'a>(self) -> Is<'a, Self>;
}

impl<T> IntoOwned for &T
where
    T: IntoOwned<Owned = T> + Clone,
{
    type Owned = T;

    fn is_owned(&self) -> bool {
        false
    }

    fn into_owned(self) -> T {
        self.borrow().clone()
    }

    fn try_as_mut(&mut self) -> Option<&mut Self::Owned> {
        None
    }

    fn as_is<'a>(self) -> Is<'a, Self> {
        Is::Borrowed(self)
    }
}

impl<T> IntoOwned for &mut T
where
    T: IntoOwned<Owned = T> + Clone,
{
    type Owned = T;

    fn is_owned(&self) -> bool {
        false
    }

    fn into_owned(self) -> T {
        (*self).borrow().clone()
    }

    fn try_as_mut(&mut self) -> Option<&mut Self::Owned> {
        Some(self)
    }

    fn as_is<'a>(self) -> Is<'a, Self> {
        Is::MutBorrowed(self)
    }
}

macro_rules! impl_into_owned_for_owned_type {
    ($T: ty) => {
        impl IntoOwned for $T {
            type Owned = Self;

            fn is_owned(&self) -> bool {
                true
            }

            fn into_owned(self) -> Self {
                self
            }

            fn try_as_mut(&mut self) -> Option<&mut Self> {
                Some(self)
            }

            fn as_is<'a>(self) -> Is<'a, Self> {
                Is::Owned(self)
            }
        }
    };
}

impl_into_owned_for_owned_type!(i8);
impl_into_owned_for_owned_type!(i16);
impl_into_owned_for_owned_type!(i32);
impl_into_owned_for_owned_type!(i64);
impl_into_owned_for_owned_type!(i128);
impl_into_owned_for_owned_type!(isize);
impl_into_owned_for_owned_type!(u8);
impl_into_owned_for_owned_type!(u16);
impl_into_owned_for_owned_type!(u32);
impl_into_owned_for_owned_type!(u64);
impl_into_owned_for_owned_type!(u128);
impl_into_owned_for_owned_type!(usize);
impl_into_owned_for_owned_type!(f32);
impl_into_owned_for_owned_type!(f64);

/// Represents an owned value, an immutably borrowed reference,
/// or a mutably borrowed reference.
///
/// This `enum` is created by the [`as_is`] method on [`IntoOwned`].
/// See its documentation for more.
///
/// [`as_is`]: trait.IntoOwned.html#tymethod.as_is
/// [`IntoOwned`]: trait.IntoOwned.html
#[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub enum Is<'a, T>
where
    T: 'a + IntoOwned,
{
    Owned(T::Owned),
    Borrowed(&'a T::Owned),
    MutBorrowed(&'a mut T::Owned),
}