as-is 0.0.27

An abstraction over ownership
Documentation
//! **Development in progress**
//!
//! This crate provides a trait [`AsIs`], an enum [`Is<'a, T>`], and variants of them.
//!
//! [`AsIs`]: trait.AsIs.html
//! [`Is<'a, T>`]: enum.Is.html

#![no_std]

#[cfg(feature = "alloc")]
extern crate alloc;

mod borrow;
pub use borrow::{BorrowAsIs, BorrowMutAsIs, Owned, ToOwnedMut};

#[cfg_attr(feature = "alloc", doc(no_inline))]
pub use borrow::ToOwned;

mod is;
mod is_cow;
mod is_mut;

pub use is::Is;
pub use is_cow::IsCow;
pub use is_mut::IsMut;

mod foreign;
pub use foreign::{StringStub, VecStub};

#[cfg(test)]
mod tests;

#[cfg(feature = "alloc")]
use alloc::borrow::Cow;

use core::borrow::{Borrow, BorrowMut};

/// Used to do a cheap conversion into [`Is<'a, T>`] in a generic context.
///
/// [`Is<'a, T>`]: enum.Is.html
pub trait AsIs: Sized + BorrowAsIs {
    /// Converts `self` into an [`Is<'a, Self::Is>`].
    ///
    /// [`Is<'a, Self::Is>`]: enum.Is.html
    #[allow(clippy::wrong_self_convention)]
    fn as_is<'a>(self) -> Is<'a, Self::Is>
    where
        Self: 'a;

    /// Converts `self` into an [`Is<'a, B>`].
    ///
    /// [`Is<'a, B>`]: enum.Is.html
    fn into_is<'a, B: ?Sized>(self) -> Is<'a, B>
    where
        Self: 'a,
        Self::Is: BorrowMut<B>,
        B: ToOwned<Owned = Owned<Self>>,
    {
        match self.as_is() {
            Is::Owned(x) => Is::Owned(x),
            Is::Borrowed(x) => Is::Borrowed(x.borrow()),
            Is::MutBorrowed(x) => Is::MutBorrowed(x.borrow_mut()),
        }
    }

    /// Converts `self` into an [`IsCow<'a, B>`].
    ///
    /// [`IsCow<'a, B>`]: enum.IsCow.html
    fn into_is_cow<'a, B: ?Sized>(self) -> IsCow<'a, B>
    where
        Self: 'a,
        Self::Is: Borrow<B>,
        B: ToOwned<Owned = Owned<Self>>,
    {
        match self.as_is() {
            Is::Owned(x) => IsCow::Owned(x),
            Is::Borrowed(x) => IsCow::Borrowed(x.borrow()),
            Is::MutBorrowed(x) => IsCow::Borrowed((*x).borrow()),
        }
    }

    /// Converts `self` into an [`IsMut<'a, B>`] without cloning if possible.
    ///
    /// # Errors
    ///
    /// If `self` cannot be converted without cloning, returns `self` as [`Is<'a, Self::Is>`].
    ///
    /// [`IsMut<'a, B>`]: enum.IsMut.html
    /// [`Is<'a, Self::Is>`]: enum.Is.html
    fn try_into_is_mut<'a, B: ?Sized>(self) -> Result<IsMut<'a, B>, Is<'a, Self::Is>>
    where
        Self: 'a,
        Self::Is: BorrowMut<B>,
        B: ToOwned<Owned = Owned<Self>>,
    {
        match self.as_is() {
            Is::Owned(x) => Ok(IsMut::Owned(x)),
            Is::Borrowed(x) => Err(Is::Borrowed(x)),
            Is::MutBorrowed(x) => Ok(IsMut::MutBorrowed(x.borrow_mut())),
        }
    }

    /// Converts `self` into an owned data without cloning if possible.
    ///
    /// # Errors
    ///
    /// If `self` cannot be converted without cloning, returns `self` as [`Is<'a, Self::Is>`].
    ///
    /// [`Is<'a, Self::Is>`]: enum.Is.html
    fn try_into_owned<'a>(self) -> Result<Owned<Self>, Is<'a, Self::Is>>
    where
        Self: 'a,
    {
        match self.as_is() {
            Is::Owned(x) => Ok(x),
            Is::Borrowed(x) => Err(Is::Borrowed(x)),
            Is::MutBorrowed(x) => Err(Is::MutBorrowed(x)),
        }
    }

    /// Converts `self` into a [`Cow<'a, B>`]. Only available if the `alloc` feature
    /// is enabled.
    ///
    /// [`Cow<'a, B>`]: https://doc.rust-lang.org/alloc/borrow/enum.Cow.html
    #[cfg(feature = "alloc")]
    fn into_cow<'a, B: ?Sized>(self) -> Cow<'a, B>
    where
        Self: 'a,
        Self::Is: Borrow<B>,
        B: ToOwned<Owned = Owned<Self>>,
    {
        match self.as_is() {
            Is::Owned(x) => Cow::Owned(x),
            Is::Borrowed(x) => Cow::Borrowed(x.borrow()),
            Is::MutBorrowed(x) => Cow::Borrowed((*x).borrow()),
        }
    }
}

impl<T> AsIs for &T
where
    Self: Borrow<T::Is>,
    T: ?Sized + BorrowAsIs,
{
    fn as_is<'a>(self) -> Is<'a, T::Is>
    where
        Self: 'a,
    {
        match self.borrow_or_clone() {
            IsCow::Owned(x) => Is::Owned(x),
            IsCow::Borrowed(x) => Is::Borrowed(x),
        }
    }
}

impl<T> AsIs for &mut T
where
    Self: Borrow<T::Is>,
    T: ?Sized + BorrowMutAsIs,
{
    fn as_is<'a>(self) -> Is<'a, T::Is>
    where
        Self: 'a,
    {
        match self.borrow_or_clone() {
            IsCow::Owned(x) => Is::Owned(x),
            IsCow::Borrowed(_) => Is::MutBorrowed(self.borrow_mut()),
        }
    }
}

/// Used to do a cheap conversion into [`IsMut<'a, T>`] in a generic context.
///
/// [`IsMut<'a, T>`]: enum.IsMut.html
pub trait AsIsMut: AsIs + BorrowMutAsIs {
    /// Converts `self` into an [`IsMut<'a, Self::Is>`].
    ///
    /// # Panics
    ///
    /// If [`self.as_is()`] returns an [`Is::Borrowed`] variant.
    ///
    /// [`IsMut<'a, Self::Is>`]: enum.IsMut.html
    /// [`self.as_is()`]: trait.AsIs.html#tymethod.as_is
    /// [`Is::Borrowed`]: enum.Is.html#variant.Borrowed
    #[allow(clippy::wrong_self_convention)]
    fn as_is_mut<'a>(self) -> IsMut<'a, Self::Is>
    where
        Self: 'a,
    {
        match self.as_is() {
            Is::Owned(x) => IsMut::Owned(x),
            Is::Borrowed(_) => {
                panic!("Is::Borrowed variant returned from an AsIsMut type")
            }
            Is::MutBorrowed(x) => IsMut::MutBorrowed(x),
        }
    }

    /// Converts `self` into an [`IsMut<'a, B>`].
    ///
    /// [`IsMut<'a, B>`]: enum.IsMut.html
    fn into_is_mut<'a, B: ?Sized>(self) -> IsMut<'a, B>
    where
        Self: 'a,
        Self::Is: BorrowMut<B>,
        B: ToOwned<Owned = Owned<Self>>,
    {
        match self.as_is_mut() {
            IsMut::Owned(x) => IsMut::Owned(x),
            IsMut::MutBorrowed(x) => IsMut::MutBorrowed(x.borrow_mut()),
        }
    }
}

impl<T> AsIsMut for T where Self: AsIs + BorrowMutAsIs {}