as-is 0.0.4-alpha

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;

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

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

pub mod to_owned_stub;

#[cfg(not(feature = "alloc"))]
use crate::to_owned_stub::ToOwnedStub as ToOwned;
#[cfg(feature = "alloc")]
use alloc::borrow::{Cow, ToOwned};

/// Used to do a cheap conversion into [`Is<'a, T>`] in a generic context.
///
/// [`Is<'a, T>`]: enum.Is.html
pub trait AsIs: Sized + Borrow<Self::Is> {
    /// The base type associated with both owned and borrowed data.
    type Is: ?Sized + ToOwned;

    /// 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;

    /// Immutably borrows from `self` by default, but can be overridden to clone `self` if
    /// appropriate.
    fn borrow_or_clone<B: ?Sized>(&self) -> IsCow<'_, B>
    where
        Self::Is: Borrow<B>,
        Owned<Self>: Borrow<B>,
        B: ToOwned<Owned = Owned<Self>>,
    {
        IsCow::Borrowed(self.borrow().borrow())
    }

    /// 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: Borrow<B> + 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()),
        }
    }

    /// If possible, converts `self` into an [`IsMut<'a, B>`] without cloning if possible,
    /// otherwise returns `self` as is.
    ///
    /// [`IsMut<'a, B>`]: enum.IsMut.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())),
        }
    }

    /// If possible, converts `self` into an owned data without cloning,
    /// otherwise returns `sefl` as is.
    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>`].
    ///
    /// [`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()),
        }
    }
}

/// A type that can be converted into [`IsMut<'a, T>`] without cloning.
///
/// [`IsMut<'a, T>`]: enum.IsMut.html
pub trait AsIsMut: AsIs + BorrowMut<Self::Is> {
    /// Converts `self` into an [`IsMut<'a, Self::Is>`].
    ///
    /// # Panic
    /// Default implementation 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(_) => unreachable!(),
            Is::MutBorrowed(x) => IsMut::MutBorrowed(x),
        }
    }

    /// Mutably borrows from `self` by default, but clones `self` if [`self.borrow_or_clone()`] is overridden to clone `self`.
    ///
    /// [`self.borrow_or_clone()`]: trait.AsIs.html#method.borrow_or_clone
    fn borrow_mut_or_clone<B: ?Sized>(&mut self) -> IsMut<'_, B>
    where
        Self::Is: BorrowMut<B>,
        Owned<Self>: BorrowMut<B>,
        B: ToOwned<Owned = Owned<Self>>,
    {
        match self.borrow_or_clone() {
            IsCow::Owned(x) => IsMut::Owned(x),
            IsCow::Borrowed(_) => IsMut::MutBorrowed(self.borrow_mut().borrow_mut()),
        }
    }

    /// 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()),
        }
    }
}

/// The resulting type after obtaining ownership.
pub type Owned<T> = <<T as AsIs>::Is as ToOwned>::Owned;

impl<T: ?Sized> AsIs for &T
where
    T: ToOwned,
    T::Owned: AsIs<Is = T::Owned>,
{
    type Is = T;

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

impl<T: ?Sized> AsIs for &mut T
where
    T: ToOwned,
    T::Owned: AsIs<Is = T::Owned>,
{
    type Is = T;

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

impl<T: ?Sized> AsIsMut for &mut T
where
    T: ToOwned,
    T::Owned: AsIs<Is = T::Owned>,
{
}

/// Represents an owned, immutably borrowed, or mutably borrowed data.
#[derive(Debug)]
pub enum Is<'a, T: ?Sized>
where
    T: ToOwned,
{
    /// Owned data.
    Owned(T::Owned),
    /// Immutably borrowed data.
    Borrowed(&'a T),
    /// Mutably borrowed data.
    MutBorrowed(&'a mut T),
}

/// Represents an owned or an immutably borrowed data.
#[derive(Debug)]
pub enum IsCow<'a, T: ?Sized>
where
    T: ToOwned,
{
    /// Owned data.
    Owned(T::Owned),
    /// Immutably borrowed data.
    Borrowed(&'a T),
}

/// Represents an owned or a mutably borrowed data.
#[derive(Debug)]
pub enum IsMut<'a, T: ?Sized>
where
    T: ToOwned,
{
    /// Owned data.
    Owned(T::Owned),
    /// Mutably borrowed data.
    MutBorrowed(&'a mut T),
}

mod impl_for_foreign;
mod impl_for_is;
mod impl_for_is_cow;
mod impl_for_is_mut;