Skip to main content

frunk_core/
generic.rs

1//! This module holds the machinery behind `Generic`.
2//!
3//! It contains the `Generic` trait and some helper methods for using the
4//! `Generic` trait without having to use universal function call syntax.
5//!
6//! # Examples
7//!
8//! ```rust
9//! use frunk::Generic;
10//!
11//! # fn main() {
12//! #[derive(Generic)]
13//! struct ApiPerson<'a> {
14//!     FirstName: &'a str,
15//!     LastName: &'a str,
16//!     Age: usize,
17//! }
18//!
19//! #[derive(Generic)]
20//! struct DomainPerson<'a> {
21//!     first_name: &'a str,
22//!     last_name: &'a str,
23//!     age: usize,
24//! }
25//!
26//! let a_person = ApiPerson {
27//!     FirstName: "Joe",
28//!     LastName: "Blow",
29//!     Age: 30,
30//! };
31//! let d_person: DomainPerson = frunk::convert_from(a_person); // done
32//! # }
33
34use crate::coproduct::Coproduct;
35use crate::hlist::HNil;
36
37/// A trait that converts from a type to a generic representation.
38///
39/// For the most part, you should be using the derivation that is available
40/// through `frunk_derive` to generate instances of this trait for your types.
41///
42/// # Laws
43///
44/// Any implementation of `Generic` must satisfy the following two laws:
45///
46/// 1. `forall x : Self. x == Generic::from(Generic::into(x))`
47/// 2. `forall y : Repr. y == Generic::into(Generic::from(y))`
48///
49/// That is, `from` and `into` should make up an isomorphism between
50/// `Self` and the representation type `Repr`.
51///
52/// # Examples
53///
54/// ```rust
55/// use frunk::Generic;
56///
57/// # fn main() {
58/// #[derive(Generic)]
59/// struct ApiPerson<'a> {
60///     FirstName: &'a str,
61///     LastName: &'a str,
62///     Age: usize,
63/// }
64///
65/// #[derive(Generic)]
66/// struct DomainPerson<'a> {
67///     first_name: &'a str,
68///     last_name: &'a str,
69///     age: usize,
70/// }
71///
72/// let a_person = ApiPerson {
73///     FirstName: "Joe",
74///     LastName: "Blow",
75///     Age: 30,
76/// };
77/// let d_person: DomainPerson = frunk::convert_from(a_person); // done
78/// # }
79/// ```
80pub trait Generic {
81    /// The generic representation type.
82    type Repr;
83
84    /// Convert a value to its representation type `Repr`.
85    fn into(self) -> Self::Repr;
86
87    /// Convert a value's representation type `Repr` to the value's type.
88    fn from(repr: Self::Repr) -> Self;
89
90    /// Convert a value to another type provided that they have
91    /// the same representation type.
92    fn convert_from<Src>(src: Src) -> Self
93    where
94        Self: Sized,
95        Src: Generic<Repr = Self::Repr>,
96    {
97        let repr = <Src as Generic>::into(src);
98        <Self as Generic>::from(repr)
99    }
100
101    /// Maps the given value of type `Self` by first transforming it to
102    /// the representation type `Repr`, then applying a `mapper` function
103    /// on `Repr` and finally transforming it back to a value of type `Self`.
104    fn map_repr<Mapper>(self, mapper: Mapper) -> Self
105    where
106        Self: Sized,
107        Mapper: FnOnce(Self::Repr) -> Self::Repr,
108    {
109        Self::from(mapper(self.into()))
110    }
111
112    /// Maps the given value of type `Self` by first transforming it
113    /// a type `Inter` that has the same representation type as `Self`,
114    /// then applying a `mapper` function on `Inter` and finally transforming
115    /// it back to a value of type `Self`.
116    fn map_inter<Inter, Mapper>(self, mapper: Mapper) -> Self
117    where
118        Self: Sized,
119        Inter: Generic<Repr = Self::Repr>,
120        Mapper: FnOnce(Inter) -> Inter,
121    {
122        Self::convert_from(mapper(Inter::convert_from(self)))
123    }
124}
125
126type UnaryVariant<T> = crate::HList!(T);
127type GenericOptionRepr<T> = crate::Coprod!(HNil, UnaryVariant<T>);
128type GenericResultRepr<T, E> = crate::Coprod!(UnaryVariant<T>, UnaryVariant<E>);
129type GenericBoolRepr = crate::Coprod!(HNil, HNil);
130
131impl<T> Generic for Option<T> {
132    type Repr = GenericOptionRepr<T>;
133
134    #[inline(always)]
135    fn into(self) -> Self::Repr {
136        match self {
137            None => Coproduct::Inl(crate::hlist![]),
138            Some(value) => Coproduct::Inr(Coproduct::Inl(crate::hlist![value])),
139        }
140    }
141
142    #[inline(always)]
143    fn from(repr: Self::Repr) -> Self {
144        match repr {
145            Coproduct::Inl(crate::hlist_pat![]) => None,
146            Coproduct::Inr(Coproduct::Inl(crate::hlist_pat![value])) => Some(value),
147            Coproduct::Inr(Coproduct::Inr(cnil)) => match cnil {},
148        }
149    }
150}
151
152impl<T, E> Generic for Result<T, E> {
153    type Repr = GenericResultRepr<T, E>;
154
155    #[inline(always)]
156    fn into(self) -> Self::Repr {
157        match self {
158            Ok(value) => Coproduct::Inl(crate::hlist![value]),
159            Err(value) => Coproduct::Inr(Coproduct::Inl(crate::hlist![value])),
160        }
161    }
162
163    #[inline(always)]
164    fn from(repr: Self::Repr) -> Self {
165        match repr {
166            Coproduct::Inl(crate::hlist_pat![value]) => Ok(value),
167            Coproduct::Inr(Coproduct::Inl(crate::hlist_pat![value])) => Err(value),
168            Coproduct::Inr(Coproduct::Inr(cnil)) => match cnil {},
169        }
170    }
171}
172
173impl Generic for bool {
174    type Repr = GenericBoolRepr;
175
176    #[inline(always)]
177    fn into(self) -> Self::Repr {
178        match self {
179            false => Coproduct::Inl(crate::hlist![]),
180            true => Coproduct::Inr(Coproduct::Inl(crate::hlist![])),
181        }
182    }
183
184    #[inline(always)]
185    fn from(repr: Self::Repr) -> Self {
186        match repr {
187            Coproduct::Inl(crate::hlist_pat![]) => false,
188            Coproduct::Inr(Coproduct::Inl(crate::hlist_pat![])) => true,
189            Coproduct::Inr(Coproduct::Inr(cnil)) => match cnil {},
190        }
191    }
192}
193
194/// Given a generic representation `Repr` of a `Dst`, returns `Dst`.
195pub fn from_generic<Dst, Repr>(repr: Repr) -> Dst
196where
197    Dst: Generic<Repr = Repr>,
198{
199    <Dst as Generic>::from(repr)
200}
201
202/// Given a value of type `Src`, returns its generic representation `Repr`.
203pub fn into_generic<Src, Repr>(src: Src) -> Repr
204where
205    Src: Generic<Repr = Repr>,
206{
207    <Src as Generic>::into(src)
208}
209
210/// Converts one type `Src` into another type `Dst` assuming they have the same
211/// representation type `Repr`.
212pub fn convert_from<Src, Dst, Repr>(src: Src) -> Dst
213where
214    Src: Generic<Repr = Repr>,
215    Dst: Generic<Repr = Repr>,
216{
217    <Dst as Generic>::convert_from(src)
218}
219
220/// Maps a value of a given type `Origin` using a function on
221/// the representation type `Repr` of `Origin`.
222pub fn map_repr<Origin, Mapper>(val: Origin, mapper: Mapper) -> Origin
223where
224    Origin: Generic,
225    Mapper: FnOnce(Origin::Repr) -> Origin::Repr,
226{
227    <Origin as Generic>::map_repr(val, mapper)
228}
229
230/// Maps a value of a given type `Origin` using a function on
231/// a type `Inter` which has the same representation type of `Origin`.
232///
233/// Note that the compiler will have a hard time inferring the type variable
234/// `Inter`. Thus, using `map_inter` is mostly effective if the type is
235/// constrained by the input function or by the body of a lambda.
236pub fn map_inter<Inter, Origin, Mapper>(val: Origin, mapper: Mapper) -> Origin
237where
238    Origin: Generic,
239    Inter: Generic<Repr = Origin::Repr>,
240    Mapper: FnOnce(Inter) -> Inter,
241{
242    <Origin as Generic>::map_inter(val, mapper)
243}