frunk_core 0.5.0

Frunk core provides developers with HList, Coproduct, LabelledGeneric and Generic
Documentation
//! This module holds the machinery behind `Generic`.
//!
//! It contains the `Generic` trait and some helper methods for using the
//! `Generic` trait without having to use universal function call syntax.
//!
//! # Examples
//!
//! ```rust
//! use frunk::Generic;
//!
//! # fn main() {
//! #[derive(Generic)]
//! struct ApiPerson<'a> {
//!     FirstName: &'a str,
//!     LastName: &'a str,
//!     Age: usize,
//! }
//!
//! #[derive(Generic)]
//! struct DomainPerson<'a> {
//!     first_name: &'a str,
//!     last_name: &'a str,
//!     age: usize,
//! }
//!
//! let a_person = ApiPerson {
//!     FirstName: "Joe",
//!     LastName: "Blow",
//!     Age: 30,
//! };
//! let d_person: DomainPerson = frunk::convert_from(a_person); // done
//! # }

use crate::coproduct::Coproduct;
use crate::hlist::HNil;

/// A trait that converts from a type to a generic representation.
///
/// For the most part, you should be using the derivation that is available
/// through `frunk_derive` to generate instances of this trait for your types.
///
/// # Laws
///
/// Any implementation of `Generic` must satisfy the following two laws:
///
/// 1. `forall x : Self. x == Generic::from(Generic::into(x))`
/// 2. `forall y : Repr. y == Generic::into(Generic::from(y))`
///
/// That is, `from` and `into` should make up an isomorphism between
/// `Self` and the representation type `Repr`.
///
/// # Examples
///
/// ```rust
/// use frunk::Generic;
///
/// # fn main() {
/// #[derive(Generic)]
/// struct ApiPerson<'a> {
///     FirstName: &'a str,
///     LastName: &'a str,
///     Age: usize,
/// }
///
/// #[derive(Generic)]
/// struct DomainPerson<'a> {
///     first_name: &'a str,
///     last_name: &'a str,
///     age: usize,
/// }
///
/// let a_person = ApiPerson {
///     FirstName: "Joe",
///     LastName: "Blow",
///     Age: 30,
/// };
/// let d_person: DomainPerson = frunk::convert_from(a_person); // done
/// # }
/// ```
pub trait Generic {
    /// The generic representation type.
    type Repr;

    /// Convert a value to its representation type `Repr`.
    fn into(self) -> Self::Repr;

    /// Convert a value's representation type `Repr` to the value's type.
    fn from(repr: Self::Repr) -> Self;

    /// Convert a value to another type provided that they have
    /// the same representation type.
    fn convert_from<Src>(src: Src) -> Self
    where
        Self: Sized,
        Src: Generic<Repr = Self::Repr>,
    {
        let repr = <Src as Generic>::into(src);
        <Self as Generic>::from(repr)
    }

    /// Maps the given value of type `Self` by first transforming it to
    /// the representation type `Repr`, then applying a `mapper` function
    /// on `Repr` and finally transforming it back to a value of type `Self`.
    fn map_repr<Mapper>(self, mapper: Mapper) -> Self
    where
        Self: Sized,
        Mapper: FnOnce(Self::Repr) -> Self::Repr,
    {
        Self::from(mapper(self.into()))
    }

    /// Maps the given value of type `Self` by first transforming it
    /// a type `Inter` that has the same representation type as `Self`,
    /// then applying a `mapper` function on `Inter` and finally transforming
    /// it back to a value of type `Self`.
    fn map_inter<Inter, Mapper>(self, mapper: Mapper) -> Self
    where
        Self: Sized,
        Inter: Generic<Repr = Self::Repr>,
        Mapper: FnOnce(Inter) -> Inter,
    {
        Self::convert_from(mapper(Inter::convert_from(self)))
    }
}

type UnaryVariant<T> = crate::HList!(T);
type GenericOptionRepr<T> = crate::Coprod!(HNil, UnaryVariant<T>);
type GenericResultRepr<T, E> = crate::Coprod!(UnaryVariant<T>, UnaryVariant<E>);
type GenericBoolRepr = crate::Coprod!(HNil, HNil);

impl<T> Generic for Option<T> {
    type Repr = GenericOptionRepr<T>;

    #[inline(always)]
    fn into(self) -> Self::Repr {
        match self {
            None => Coproduct::Inl(crate::hlist![]),
            Some(value) => Coproduct::Inr(Coproduct::Inl(crate::hlist![value])),
        }
    }

    #[inline(always)]
    fn from(repr: Self::Repr) -> Self {
        match repr {
            Coproduct::Inl(crate::hlist_pat![]) => None,
            Coproduct::Inr(Coproduct::Inl(crate::hlist_pat![value])) => Some(value),
            Coproduct::Inr(Coproduct::Inr(cnil)) => match cnil {},
        }
    }
}

impl<T, E> Generic for Result<T, E> {
    type Repr = GenericResultRepr<T, E>;

    #[inline(always)]
    fn into(self) -> Self::Repr {
        match self {
            Ok(value) => Coproduct::Inl(crate::hlist![value]),
            Err(value) => Coproduct::Inr(Coproduct::Inl(crate::hlist![value])),
        }
    }

    #[inline(always)]
    fn from(repr: Self::Repr) -> Self {
        match repr {
            Coproduct::Inl(crate::hlist_pat![value]) => Ok(value),
            Coproduct::Inr(Coproduct::Inl(crate::hlist_pat![value])) => Err(value),
            Coproduct::Inr(Coproduct::Inr(cnil)) => match cnil {},
        }
    }
}

impl Generic for bool {
    type Repr = GenericBoolRepr;

    #[inline(always)]
    fn into(self) -> Self::Repr {
        match self {
            false => Coproduct::Inl(crate::hlist![]),
            true => Coproduct::Inr(Coproduct::Inl(crate::hlist![])),
        }
    }

    #[inline(always)]
    fn from(repr: Self::Repr) -> Self {
        match repr {
            Coproduct::Inl(crate::hlist_pat![]) => false,
            Coproduct::Inr(Coproduct::Inl(crate::hlist_pat![])) => true,
            Coproduct::Inr(Coproduct::Inr(cnil)) => match cnil {},
        }
    }
}

/// Given a generic representation `Repr` of a `Dst`, returns `Dst`.
pub fn from_generic<Dst, Repr>(repr: Repr) -> Dst
where
    Dst: Generic<Repr = Repr>,
{
    <Dst as Generic>::from(repr)
}

/// Given a value of type `Src`, returns its generic representation `Repr`.
pub fn into_generic<Src, Repr>(src: Src) -> Repr
where
    Src: Generic<Repr = Repr>,
{
    <Src as Generic>::into(src)
}

/// Converts one type `Src` into another type `Dst` assuming they have the same
/// representation type `Repr`.
pub fn convert_from<Src, Dst, Repr>(src: Src) -> Dst
where
    Src: Generic<Repr = Repr>,
    Dst: Generic<Repr = Repr>,
{
    <Dst as Generic>::convert_from(src)
}

/// Maps a value of a given type `Origin` using a function on
/// the representation type `Repr` of `Origin`.
pub fn map_repr<Origin, Mapper>(val: Origin, mapper: Mapper) -> Origin
where
    Origin: Generic,
    Mapper: FnOnce(Origin::Repr) -> Origin::Repr,
{
    <Origin as Generic>::map_repr(val, mapper)
}

/// Maps a value of a given type `Origin` using a function on
/// a type `Inter` which has the same representation type of `Origin`.
///
/// Note that the compiler will have a hard time inferring the type variable
/// `Inter`. Thus, using `map_inter` is mostly effective if the type is
/// constrained by the input function or by the body of a lambda.
pub fn map_inter<Inter, Origin, Mapper>(val: Origin, mapper: Mapper) -> Origin
where
    Origin: Generic,
    Inter: Generic<Repr = Origin::Repr>,
    Mapper: FnOnce(Inter) -> Inter,
{
    <Origin as Generic>::map_inter(val, mapper)
}