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}