frunk_core/
labelled.rs

1//! This module holds the machinery behind LabelledGeneric.
2//!
3//! A `LabelledGeneric` instance is pretty much exactly the same as a `Generic`
4//! instance, except that the generic representation should contain information
5//! about field names.
6//!
7//! Having a separate trait for `LabelledGeneric`s gives us the freedom to
8//! derive both labelled and non-labelled generic trait instances for our types.
9//!
10//! Aside from the main `LabelledGeneric` trait, this module holds helper
11//! methods that allow users to use `LabelledGeneric` without using universal
12//! function call syntax.
13//!
14//! In addition, this module holds macro-generated enums that map to letters
15//! in field names (identifiers).
16//!
17//! # Examples
18//!
19//! ```
20//! # fn main() {
21//! use frunk::labelled::chars::*;
22//! use frunk_core::field;
23//!
24//! // Optionally alias our tuple that represents our type-level string
25//! type name = (n, a, m, e);
26//! let labelled = field![name, "Lloyd"];
27//! assert_eq!(labelled.name, "name");
28//! assert_eq!(labelled.value, "Lloyd")
29//! # }
30//! ```
31//!
32//! A more common usage is to use `LabelledGeneric` to transform structs that
33//! have mismatched fields!
34//!
35//! ```
36//! // required when using custom derives
37//! use frunk::LabelledGeneric;
38//!
39//! # fn main() {
40//! #[derive(LabelledGeneric)]
41//! struct NewUser<'a> {
42//!     first_name: &'a str,
43//!     last_name: &'a str,
44//!     age: usize,
45//! }
46//!
47//! // Notice that the fields are mismatched in terms of ordering
48//! // *and* also in terms of the number of fields.
49//! #[derive(LabelledGeneric)]
50//! struct ShortUser<'a> {
51//!     last_name: &'a str,
52//!     first_name: &'a str,
53//! }
54//!
55//! let n_user = NewUser {
56//!     first_name: "Joe",
57//!     last_name: "Blow",
58//!     age: 30,
59//! };
60//!
61//! // transform_from automagically sculpts the labelled generic
62//! // representation of the source object to that of the target type
63//! let s_user: ShortUser = frunk::transform_from(n_user); // done
64//! # }
65//! ```
66//!
67//! If you have the need to transform types that are similarly-shaped recursively, then
68//! use the Transmogrifier trait.
69//!
70//! ```
71//! // required when using custom derives
72//! # fn main() {
73//! use frunk::labelled::Transmogrifier;
74//! use frunk::LabelledGeneric;
75//!
76//! #[derive(LabelledGeneric)]
77//! struct InternalPhoneNumber {
78//!     emergency: Option<usize>,
79//!     main: usize,
80//!     secondary: Option<usize>,
81//! }
82//!
83//! #[derive(LabelledGeneric)]
84//! struct InternalAddress<'a> {
85//!     is_whitelisted: bool,
86//!     name: &'a str,
87//!     phone: InternalPhoneNumber,
88//! }
89//!
90//! #[derive(LabelledGeneric)]
91//! struct InternalUser<'a> {
92//!     name: &'a str,
93//!     age: usize,
94//!     address: InternalAddress<'a>,
95//!     is_banned: bool,
96//! }
97//!
98//! #[derive(LabelledGeneric, PartialEq, Debug)]
99//! struct ExternalPhoneNumber {
100//!     main: usize,
101//! }
102//!
103//! #[derive(LabelledGeneric, PartialEq, Debug)]
104//! struct ExternalAddress<'a> {
105//!     name: &'a str,
106//!     phone: ExternalPhoneNumber,
107//! }
108//!
109//! #[derive(LabelledGeneric, PartialEq, Debug)]
110//! struct ExternalUser<'a> {
111//!     age: usize,
112//!     address: ExternalAddress<'a>,
113//!     name: &'a str,
114//! }
115//!
116//! let internal_user = InternalUser {
117//!     name: "John",
118//!     age: 10,
119//!     address: InternalAddress {
120//!         is_whitelisted: true,
121//!         name: "somewhere out there",
122//!         phone: InternalPhoneNumber {
123//!             main: 1234,
124//!             secondary: None,
125//!             emergency: Some(5678),
126//!         },
127//!     },
128//!     is_banned: true,
129//! };
130//!
131//! /// Boilerplate-free conversion of a top-level InternalUser into an
132//! /// ExternalUser, taking care of subfield conversions as well.
133//! let external_user: ExternalUser = internal_user.transmogrify();
134//!
135//! let expected_external_user = ExternalUser {
136//!     name: "John",
137//!     age: 10,
138//!     address: ExternalAddress {
139//!         name: "somewhere out there",
140//!         phone: ExternalPhoneNumber {
141//!             main: 1234,
142//!         },
143//!     }
144//! };
145//!
146//! assert_eq!(external_user, expected_external_user);
147//! # }
148//! ```
149
150use crate::hlist::*;
151use crate::indices::*;
152use crate::traits::ToRef;
153#[cfg(feature = "serde")]
154use serde::{Deserialize, Serialize};
155
156use core::fmt;
157use core::marker::PhantomData;
158
159/// A trait that converts from a type to a labelled generic representation.
160///
161/// `LabelledGeneric`s allow us to have completely type-safe,
162/// boilerplate free conversions between different structs.
163///
164/// For the most part, you should be using the derivation that is available
165/// through `frunk_derive` to generate instances of this trait for your types.
166///
167/// # Examples
168///
169/// ```rust
170/// use frunk::LabelledGeneric;
171///
172/// # fn main() {
173/// #[derive(LabelledGeneric)]
174/// struct NewUser<'a> {
175///     first_name: &'a str,
176///     last_name: &'a str,
177///     age: usize,
178/// }
179///
180/// // Notice that the fields are mismatched in terms of ordering
181/// #[derive(LabelledGeneric)]
182/// struct SavedUser<'a> {
183///     last_name: &'a str,
184///     age: usize,
185///     first_name: &'a str,
186/// }
187///
188/// let n_user = NewUser {
189///     first_name: "Joe",
190///     last_name: "Blow",
191///     age: 30,
192/// };
193///
194/// // transform_from automagically sculpts the labelled generic
195/// // representation of the source object to that of the target type
196/// let s_user: SavedUser = frunk::transform_from(n_user); // done
197/// # }
198pub trait LabelledGeneric {
199    /// The labelled generic representation type.
200    type Repr;
201
202    /// Convert a value to its representation type `Repr`.
203    fn into(self) -> Self::Repr;
204
205    /// Convert a value's labelled representation type `Repr`
206    /// to the values's type.
207    fn from(repr: Self::Repr) -> Self;
208
209    /// Convert from one type to another using a type with the same
210    /// labelled generic representation
211    #[inline(always)]
212    fn convert_from<Src>(src: Src) -> Self
213    where
214        Src: LabelledGeneric<Repr = Self::Repr>,
215        Self: Sized,
216    {
217        let repr = <Src as LabelledGeneric>::into(src);
218        <Self as LabelledGeneric>::from(repr)
219    }
220
221    /// Converts from another type A into Self assuming that A and Self have
222    /// labelled generic representations that can be sculpted into each other.
223    ///
224    /// Note that this method tosses away the "remainder" of the sculpted representation. In other
225    /// words, anything that is not needed from A gets tossed out.
226    #[deprecated(note = "obsolete, transform_from instead")]
227    fn sculpted_convert_from<A, Indices>(a: A) -> Self
228    where
229        A: LabelledGeneric,
230        Self: Sized,
231        // The labelled representation of A must be sculpt-able into the labelled representation of Self
232        <A as LabelledGeneric>::Repr: Sculptor<<Self as LabelledGeneric>::Repr, Indices>,
233    {
234        <Self as LabelledGeneric>::transform_from(a)
235    }
236
237    /// Converts from another type `Src` into `Self` assuming that `Src` and
238    /// `Self` have labelled generic representations that can be sculpted into
239    /// each other.
240    ///
241    /// Note that this method tosses away the "remainder" of the sculpted
242    /// representation. In other words, anything that is not needed from `Src`
243    /// gets tossed out.
244    #[inline(always)]
245    fn transform_from<Src, Indices>(src: Src) -> Self
246    where
247        Src: LabelledGeneric,
248        Self: Sized,
249        // The labelled representation of `Src` must be sculpt-able into the labelled representation of `Self`
250        <Src as LabelledGeneric>::Repr: Sculptor<<Self as LabelledGeneric>::Repr, Indices>,
251    {
252        let src_gen = <Src as LabelledGeneric>::into(src);
253        // We toss away the remainder.
254        let (self_gen, _): (<Self as LabelledGeneric>::Repr, _) = src_gen.sculpt();
255        <Self as LabelledGeneric>::from(self_gen)
256    }
257}
258
259pub trait IntoLabelledGeneric {
260    /// The labelled generic representation type.
261    type Repr;
262
263    /// Convert a value to its representation type `Repr`.
264    fn into(self) -> Self::Repr;
265}
266
267impl<A> IntoLabelledGeneric for A
268where
269    A: LabelledGeneric,
270{
271    type Repr = <A as LabelledGeneric>::Repr;
272
273    #[inline(always)]
274    fn into(self) -> <Self as IntoLabelledGeneric>::Repr {
275        self.into()
276    }
277}
278
279/// Given a labelled generic representation of a `Dst`, returns `Dst`
280pub fn from_labelled_generic<Dst, Repr>(repr: Repr) -> Dst
281where
282    Dst: LabelledGeneric<Repr = Repr>,
283{
284    <Dst as LabelledGeneric>::from(repr)
285}
286
287/// Given a `Src`, returns its labelled generic representation.
288pub fn into_labelled_generic<Src, Repr>(src: Src) -> Repr
289where
290    Src: LabelledGeneric<Repr = Repr>,
291{
292    <Src as LabelledGeneric>::into(src)
293}
294
295/// Converts one type into another assuming they have the same labelled generic
296/// representation.
297pub fn labelled_convert_from<Src, Dst, Repr>(src: Src) -> Dst
298where
299    Src: LabelledGeneric<Repr = Repr>,
300    Dst: LabelledGeneric<Repr = Repr>,
301{
302    <Dst as LabelledGeneric>::convert_from(src)
303}
304
305/// Converts from one type into another assuming that their labelled generic representations
306/// can be sculpted into each other.
307///
308/// The "Indices" type parameter allows the compiler to figure out that the two representations
309/// can indeed be morphed into each other.
310#[deprecated(note = "obsolete, transform_from instead")]
311pub fn sculpted_convert_from<A, B, Indices>(a: A) -> B
312where
313    A: LabelledGeneric,
314    B: LabelledGeneric,
315    // The labelled representation of A must be sculpt-able into the labelled representation of B
316    <A as LabelledGeneric>::Repr: Sculptor<<B as LabelledGeneric>::Repr, Indices>,
317{
318    <B as LabelledGeneric>::transform_from(a)
319}
320/// Converts from one type into another assuming that their labelled generic representations
321/// can be sculpted into each other.
322///
323/// The "Indices" type parameter allows the compiler to figure out that the two representations
324/// can indeed be morphed into each other.
325pub fn transform_from<Src, Dst, Indices>(src: Src) -> Dst
326where
327    Src: LabelledGeneric,
328    Dst: LabelledGeneric,
329    // The labelled representation of Src must be sculpt-able into the labelled representation of Dst
330    <Src as LabelledGeneric>::Repr: Sculptor<<Dst as LabelledGeneric>::Repr, Indices>,
331{
332    <Dst as LabelledGeneric>::transform_from(src)
333}
334
335pub mod chars {
336    //! Types for building type-level labels from character sequences.
337    //!
338    //! This is designed to be glob-imported:
339    //!
340    //! ```rust
341    //! # extern crate frunk;
342    //! # fn main() {
343    //! # #[allow(unused)]
344    //! use frunk::labelled::chars::*;
345    //! # }
346    //! ```
347
348    macro_rules! create_enums_for {
349        ($($i: ident)*) => {
350            $(
351                #[allow(non_snake_case, non_camel_case_types)]
352                #[derive(PartialEq, Debug, Eq, Clone, Copy, PartialOrd, Ord, Hash)]
353                pub enum $i {}
354            )*
355        }
356    }
357
358    // Add more as needed.
359    create_enums_for! {
360        // all valid identifier characters
361        a b c d e f g h i j k l m n o p q r s t u v w x y z
362        A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
363        _1 _2 _3 _4 _5 _6 _7 _8 _9 _0 __ _uc uc_
364    }
365
366    #[test]
367    fn simple_var_names_are_allowed() {
368        // Rust forbids variable bindings that shadow unit structs,
369        // so unit struct characters would cause a lot of trouble.
370        //
371        // Good thing I don't plan on adding reified labels. - Exp
372        let a = 3;
373        #[allow(clippy::match_single_binding)]
374        match a {
375            a => assert_eq!(a, 3),
376        }
377    }
378}
379
380/// A Label contains a type-level Name, a runtime value, and
381/// a reference to a `&'static str` name.
382///
383/// To construct one, use the `field!` macro.
384///
385/// # Examples
386///
387/// ```
388/// use frunk::labelled::chars::*;
389/// use frunk_core::field;
390/// # fn main() {
391/// let labelled = field![(n,a,m,e), "joe"];
392/// assert_eq!(labelled.name, "name");
393/// assert_eq!(labelled.value, "joe")
394/// # }
395/// ```
396#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
397#[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)]
398pub struct Field<Name, Type> {
399    name_type_holder: PhantomData<Name>,
400    pub name: &'static str,
401    pub value: Type,
402}
403
404/// A version of Field that doesn't have a type-level label, just a
405/// value-level one
406#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
407#[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)]
408pub struct ValueField<Type> {
409    pub name: &'static str,
410    pub value: Type,
411}
412
413impl<Name, Type> fmt::Debug for Field<Name, Type>
414where
415    Type: fmt::Debug,
416{
417    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
418        f.debug_struct("Field")
419            // show name without quotes
420            .field("name", &DebugAsDisplay(&self.name))
421            .field("value", &self.value)
422            .finish()
423    }
424}
425
426impl<Type> fmt::Debug for ValueField<Type>
427where
428    Type: fmt::Debug,
429{
430    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
431        f.debug_struct("ValueField")
432            // show name without quotes
433            .field("name", &DebugAsDisplay(&self.name))
434            .field("value", &self.value)
435            .finish()
436    }
437}
438
439/// Utility type that implements Debug in terms of Display.
440struct DebugAsDisplay<T>(T);
441
442impl<T: fmt::Display> fmt::Debug for DebugAsDisplay<T> {
443    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
444        fmt::Display::fmt(&self.0, f)
445    }
446}
447
448/// Returns a new Field for a given value and custom name.
449///
450/// If you don't want to provide a custom name and want to rely on the type you provide
451/// to build a name, then please use the field! macro.
452///
453/// # Examples
454///
455/// ```
456/// use frunk::labelled::chars::*;
457/// use frunk::labelled::field_with_name;
458///
459/// let l = field_with_name::<(n,a,m,e),_>("name", "joe");
460/// assert_eq!(l.value, "joe");
461/// assert_eq!(l.name, "name");
462/// ```
463pub fn field_with_name<Label, Value>(name: &'static str, value: Value) -> Field<Label, Value> {
464    Field {
465        name_type_holder: PhantomData,
466        name,
467        value,
468    }
469}
470
471/// Trait for turning a Field HList into an un-labelled HList
472pub trait IntoUnlabelled {
473    type Output;
474
475    /// Turns the current HList into an unlabelled one.
476    ///
477    /// Effectively extracts the values held inside the individual Field
478    ///
479    /// # Examples
480    ///
481    /// ```
482    /// # fn main() {
483    /// use frunk::labelled::chars::*;
484    /// use frunk::labelled::IntoUnlabelled;
485    /// use frunk_core::{field, hlist};
486    ///
487    /// let labelled_hlist = hlist![
488    ///     field!((n, a, m, e), "joe"),
489    ///     field!((a, g, e), 3)
490    /// ];
491    ///
492    /// let unlabelled = labelled_hlist.into_unlabelled();
493    ///
494    /// assert_eq!(unlabelled, hlist!["joe", 3])
495    /// # }
496    /// ```
497    fn into_unlabelled(self) -> Self::Output;
498}
499
500/// Implementation for HNil
501impl IntoUnlabelled for HNil {
502    type Output = HNil;
503    fn into_unlabelled(self) -> Self::Output {
504        self
505    }
506}
507
508/// Implementation when we have a non-empty HCons holding a label in its head
509impl<Label, Value, Tail> IntoUnlabelled for HCons<Field<Label, Value>, Tail>
510where
511    Tail: IntoUnlabelled,
512{
513    type Output = HCons<Value, <Tail as IntoUnlabelled>::Output>;
514
515    fn into_unlabelled(self) -> Self::Output {
516        HCons {
517            head: self.head.value,
518            tail: self.tail.into_unlabelled(),
519        }
520    }
521}
522
523/// A trait that strips type-level strings from the labels
524pub trait IntoValueLabelled {
525    type Output;
526
527    /// Turns the current HList into a value-labelled one.
528    ///
529    /// Effectively extracts the names and values held inside the individual Fields
530    /// and puts them into ValueFields, which do not have type-level names.
531    ///
532    /// # Examples
533    ///
534    /// ```
535    /// # fn main() {
536    /// use frunk::labelled::{ValueField, IntoValueLabelled};
537    /// use frunk::labelled::chars::*;
538    /// use frunk_core::{field, hlist, HList};
539    ///
540    /// let labelled_hlist = hlist![
541    ///     field!((n, a, m, e), "joe"),
542    ///     field!((a, g, e), 3)
543    /// ];
544    /// // Notice the lack of type-level names
545    /// let value_labelled: HList![ValueField<&str>, ValueField<isize>] = labelled_hlist.into_value_labelled();
546    ///
547    /// assert_eq!(
548    ///   value_labelled,
549    ///   hlist![
550    ///     ValueField {
551    ///       name: "name",
552    ///       value: "joe",
553    ///     },
554    ///     ValueField {
555    ///       name: "age",
556    ///       value: 3,
557    ///     },
558    /// ]);
559    /// # }
560    /// ```
561    fn into_value_labelled(self) -> Self::Output;
562}
563
564impl IntoValueLabelled for HNil {
565    type Output = HNil;
566    fn into_value_labelled(self) -> Self::Output {
567        self
568    }
569}
570
571impl<Label, Value, Tail> IntoValueLabelled for HCons<Field<Label, Value>, Tail>
572where
573    Tail: IntoValueLabelled,
574{
575    type Output = HCons<ValueField<Value>, <Tail as IntoValueLabelled>::Output>;
576
577    fn into_value_labelled(self) -> Self::Output {
578        HCons {
579            head: ValueField {
580                name: self.head.name,
581                value: self.head.value,
582            },
583            tail: self.tail.into_value_labelled(),
584        }
585    }
586}
587
588/// Trait for plucking out a `Field` from a type by type-level `TargetKey`.
589pub trait ByNameFieldPlucker<TargetKey, Index> {
590    type TargetValue;
591    type Remainder;
592
593    /// Returns a pair consisting of the value pointed to by the target key and the remainder.
594    fn pluck_by_name(self) -> (Field<TargetKey, Self::TargetValue>, Self::Remainder);
595}
596
597/// Implementation when the pluck target key is in the head.
598impl<K, V, Tail> ByNameFieldPlucker<K, Here> for HCons<Field<K, V>, Tail> {
599    type TargetValue = V;
600    type Remainder = Tail;
601
602    #[inline(always)]
603    fn pluck_by_name(self) -> (Field<K, Self::TargetValue>, Self::Remainder) {
604        let field = field_with_name(self.head.name, self.head.value);
605        (field, self.tail)
606    }
607}
608
609/// Implementation when the pluck target key is in the tail.
610impl<Head, Tail, K, TailIndex> ByNameFieldPlucker<K, There<TailIndex>> for HCons<Head, Tail>
611where
612    Tail: ByNameFieldPlucker<K, TailIndex>,
613{
614    type TargetValue = <Tail as ByNameFieldPlucker<K, TailIndex>>::TargetValue;
615    type Remainder = HCons<Head, <Tail as ByNameFieldPlucker<K, TailIndex>>::Remainder>;
616
617    #[inline(always)]
618    fn pluck_by_name(self) -> (Field<K, Self::TargetValue>, Self::Remainder) {
619        let (target, tail_remainder) =
620            <Tail as ByNameFieldPlucker<K, TailIndex>>::pluck_by_name(self.tail);
621        (
622            target,
623            HCons {
624                head: self.head,
625                tail: tail_remainder,
626            },
627        )
628    }
629}
630
631/// Implementation when target is reference and the pluck target key is in the head.
632impl<'a, K, V, Tail: ToRef<'a>> ByNameFieldPlucker<K, Here> for &'a HCons<Field<K, V>, Tail> {
633    type TargetValue = &'a V;
634    type Remainder = <Tail as ToRef<'a>>::Output;
635
636    #[inline(always)]
637    fn pluck_by_name(self) -> (Field<K, Self::TargetValue>, Self::Remainder) {
638        let field = field_with_name(self.head.name, &self.head.value);
639        (field, self.tail.to_ref())
640    }
641}
642
643/// Implementation when target is reference and the pluck target key is in the tail.
644impl<'a, Head, Tail, K, TailIndex> ByNameFieldPlucker<K, There<TailIndex>> for &'a HCons<Head, Tail>
645where
646    &'a Tail: ByNameFieldPlucker<K, TailIndex>,
647{
648    type TargetValue = <&'a Tail as ByNameFieldPlucker<K, TailIndex>>::TargetValue;
649    type Remainder = HCons<&'a Head, <&'a Tail as ByNameFieldPlucker<K, TailIndex>>::Remainder>;
650
651    #[inline(always)]
652    fn pluck_by_name(self) -> (Field<K, Self::TargetValue>, Self::Remainder) {
653        let (target, tail_remainder) =
654            <&'a Tail as ByNameFieldPlucker<K, TailIndex>>::pluck_by_name(&self.tail);
655        (
656            target,
657            HCons {
658                head: &self.head,
659                tail: tail_remainder,
660            },
661        )
662    }
663}
664
665/// Trait for transmogrifying a `Source` type into a `Target` type.
666///
667/// What is "transmogrifying"? In this context, it means to convert some data of type `A`
668/// into data of type `B`, in a typesafe, recursive way, as long as `A` and `B` are "similarly-shaped".
669/// In other words, as long as `B`'s fields and their subfields are subsets of `A`'s fields and
670/// their respective subfields, then `A` can be turned into `B`.
671///
672/// # Example
673///
674/// ```
675/// // required when using custom derives
676/// # fn main() {
677/// use frunk::LabelledGeneric;
678/// use frunk::labelled::Transmogrifier;
679/// #[derive(LabelledGeneric)]
680/// struct InternalPhoneNumber {
681///     emergency: Option<usize>,
682///     main: usize,
683///     secondary: Option<usize>,
684/// }
685///
686/// #[derive(LabelledGeneric)]
687/// struct InternalAddress<'a> {
688///     is_whitelisted: bool,
689///     name: &'a str,
690///     phone: InternalPhoneNumber,
691/// }
692///
693/// #[derive(LabelledGeneric)]
694/// struct InternalUser<'a> {
695///     name: &'a str,
696///     age: usize,
697///     address: InternalAddress<'a>,
698///     is_banned: bool,
699/// }
700///
701/// #[derive(LabelledGeneric, PartialEq, Debug)]
702/// struct ExternalPhoneNumber {
703///     main: usize,
704/// }
705///
706/// #[derive(LabelledGeneric, PartialEq, Debug)]
707/// struct ExternalAddress<'a> {
708///     name: &'a str,
709///     phone: ExternalPhoneNumber,
710/// }
711///
712/// #[derive(LabelledGeneric, PartialEq, Debug)]
713/// struct ExternalUser<'a> {
714///     age: usize,
715///     address: ExternalAddress<'a>,
716///     name: &'a str,
717/// }
718///
719/// let internal_user = InternalUser {
720///     name: "John",
721///     age: 10,
722///     address: InternalAddress {
723///         is_whitelisted: true,
724///         name: "somewhere out there",
725///         phone: InternalPhoneNumber {
726///             main: 1234,
727///             secondary: None,
728///             emergency: Some(5678),
729///         },
730///     },
731///     is_banned: true,
732/// };
733///
734/// /// Boilerplate-free conversion of a top-level InternalUser into an
735/// /// ExternalUser, taking care of subfield conversions as well.
736/// let external_user: ExternalUser = internal_user.transmogrify();
737///
738/// let expected_external_user = ExternalUser {
739///     name: "John",
740///     age: 10,
741///     address: ExternalAddress {
742///         name: "somewhere out there",
743///         phone: ExternalPhoneNumber {
744///             main: 1234,
745///         },
746///     }
747/// };
748///
749/// assert_eq!(external_user, expected_external_user);
750/// # }
751/// ```
752///
753/// Credit:
754/// 1. Haskell "transmogrify" Github repo: <https://github.com/ivan-m/transmogrify>
755pub trait Transmogrifier<Target, TransmogrifyIndexIndices> {
756    /// Consume this current object and return an object of the Target type.
757    ///
758    /// Although similar to sculpting, transmogrifying does its job recursively.
759    fn transmogrify(self) -> Target;
760}
761
762/// Implementation of `Transmogrifier` for identity plucked `Field` to `Field` Transforms.
763impl<Key, SourceValue> Transmogrifier<SourceValue, IdentityTransMog> for Field<Key, SourceValue> {
764    #[inline(always)]
765    fn transmogrify(self) -> SourceValue {
766        self.value
767    }
768}
769
770/// Implementations of `Transmogrifier` that allow recursion through stdlib container types.
771#[cfg(feature = "alloc")]
772mod _alloc {
773    use super::MappingIndicesWrapper;
774    use super::{Field, Transmogrifier};
775    use alloc::boxed::Box;
776    use alloc::collections::{LinkedList, VecDeque};
777    use alloc::vec::Vec;
778
779    macro_rules! transmogrify_seq {
780        ($container:ident) => {
781            /// Implementation of `Transmogrifier` that maps over a `$container` in a `Field`, transmogrifying the
782            /// elements on the way past.
783            impl<Key, Source, Target, InnerIndices>
784                Transmogrifier<$container<Target>, MappingIndicesWrapper<InnerIndices>>
785                for Field<Key, $container<Source>>
786            where
787                Source: Transmogrifier<Target, InnerIndices>,
788            {
789                fn transmogrify(self) -> $container<Target> {
790                    self.value.into_iter().map(|e| e.transmogrify()).collect()
791                }
792            }
793        };
794    }
795
796    transmogrify_seq!(Vec);
797    transmogrify_seq!(LinkedList);
798    transmogrify_seq!(VecDeque);
799
800    /// Implementation of `Transmogrifier` that maps over an `Box` in a `Field`, transmogrifying the
801    /// contained element on the way past.
802    impl<Key, Source, Target, InnerIndices>
803        Transmogrifier<Box<Target>, MappingIndicesWrapper<InnerIndices>> for Field<Key, Box<Source>>
804    where
805        Source: Transmogrifier<Target, InnerIndices>,
806    {
807        fn transmogrify(self) -> Box<Target> {
808            Box::new(self.value.transmogrify())
809        }
810    }
811}
812
813/// Implementation of `Transmogrifier` that maps over an `Option` in a `Field`, transmogrifying the
814/// contained element on the way past if present.
815impl<Key, Source, Target, InnerIndices>
816    Transmogrifier<Option<Target>, MappingIndicesWrapper<InnerIndices>>
817    for Field<Key, Option<Source>>
818where
819    Source: Transmogrifier<Target, InnerIndices>,
820{
821    fn transmogrify(self) -> Option<Target> {
822        self.value.map(|e| e.transmogrify())
823    }
824}
825
826/// Implementation of `Transmogrifier` for when the `Target` is empty and the `Source` is empty.
827impl Transmogrifier<HNil, HNil> for HNil {
828    #[inline(always)]
829    fn transmogrify(self) -> HNil {
830        HNil
831    }
832}
833
834/// Implementation of `Transmogrifier` for when the `Target` is empty and the `Source` is non-empty.
835impl<SourceHead, SourceTail> Transmogrifier<HNil, HNil> for HCons<SourceHead, SourceTail> {
836    #[inline(always)]
837    fn transmogrify(self) -> HNil {
838        HNil
839    }
840}
841
842/// Implementation of `Transmogrifier` for when the target is an `HList`, and the `Source` is a plucked
843/// `HList`.
844impl<
845        SourceHead,
846        SourceTail,
847        TargetName,
848        TargetHead,
849        TargetTail,
850        TransmogHeadIndex,
851        TransmogTailIndices,
852    > Transmogrifier<HCons<TargetHead, TargetTail>, HCons<TransmogHeadIndex, TransmogTailIndices>>
853    for Field<TargetName, HCons<SourceHead, SourceTail>>
854where
855    HCons<SourceHead, SourceTail>: Transmogrifier<
856        HCons<TargetHead, TargetTail>,
857        HCons<TransmogHeadIndex, TransmogTailIndices>,
858    >,
859{
860    #[inline(always)]
861    fn transmogrify(self) -> HCons<TargetHead, TargetTail> {
862        self.value.transmogrify()
863    }
864}
865
866/// Non-trivial implementation of `Transmogrifier` where similarly-shaped `Source` and `Target` types are
867/// both Labelled HLists, but do not immediately transform into one another due to mis-matched
868/// fields, possibly recursively so.
869impl<
870        SourceHead,
871        SourceTail,
872        TargetHeadName,
873        TargetHeadValue,
874        TargetTail,
875        PluckSourceHeadNameIndex,
876        TransMogSourceHeadValueIndices,
877        TransMogTailIndices,
878    >
879    Transmogrifier<
880        HCons<Field<TargetHeadName, TargetHeadValue>, TargetTail>,
881        HCons<
882            DoTransmog<PluckSourceHeadNameIndex, TransMogSourceHeadValueIndices>,
883            TransMogTailIndices,
884        >,
885    > for HCons<SourceHead, SourceTail>
886where
887    // Pluck a value out of the Source by the Head Target Name
888    HCons<SourceHead, SourceTail>: ByNameFieldPlucker<TargetHeadName, PluckSourceHeadNameIndex>,
889    // The value we pluck out needs to be able to be transmogrified to the Head Target Value type
890    Field<
891        TargetHeadName,
892        <HCons<SourceHead, SourceTail> as ByNameFieldPlucker<
893            TargetHeadName,
894            PluckSourceHeadNameIndex,
895        >>::TargetValue,
896    >: Transmogrifier<TargetHeadValue, TransMogSourceHeadValueIndices>,
897    // The remainder from plucking out the Head Target Name must be able to be transmogrified to the
898    // target tail, utilising the other remaining indices
899    <HCons<SourceHead, SourceTail> as ByNameFieldPlucker<
900        TargetHeadName,
901        PluckSourceHeadNameIndex,
902    >>::Remainder: Transmogrifier<TargetTail, TransMogTailIndices>,
903{
904    #[inline(always)]
905    fn transmogrify(self) -> HCons<Field<TargetHeadName, TargetHeadValue>, TargetTail> {
906        let (source_field_for_head_target_name, remainder) = self.pluck_by_name();
907        let name = source_field_for_head_target_name.name;
908        let transmogrified_value: TargetHeadValue =
909            source_field_for_head_target_name.transmogrify();
910        let as_field: Field<TargetHeadName, TargetHeadValue> =
911            field_with_name(name, transmogrified_value);
912        HCons {
913            head: as_field,
914            tail: remainder.transmogrify(),
915        }
916    }
917}
918
919impl<Source, Target, TransmogIndices>
920    Transmogrifier<Target, LabelledGenericTransmogIndicesWrapper<TransmogIndices>> for Source
921where
922    Source: LabelledGeneric,
923    Target: LabelledGeneric,
924    <Source as LabelledGeneric>::Repr:
925        Transmogrifier<<Target as LabelledGeneric>::Repr, TransmogIndices>,
926{
927    #[inline(always)]
928    fn transmogrify(self) -> Target {
929        let source_as_repr = self.into();
930        let source_transmogged = source_as_repr.transmogrify();
931        <Target as LabelledGeneric>::from(source_transmogged)
932    }
933}
934
935// Implementation for when the source value is plucked
936impl<Source, TargetName, TargetValue, TransmogIndices>
937    Transmogrifier<TargetValue, PluckedLabelledGenericIndicesWrapper<TransmogIndices>>
938    for Field<TargetName, Source>
939where
940    Source: LabelledGeneric,
941    TargetValue: LabelledGeneric,
942    Source: Transmogrifier<TargetValue, TransmogIndices>,
943{
944    #[inline(always)]
945    fn transmogrify(self) -> TargetValue {
946        self.value.transmogrify()
947    }
948}
949
950#[cfg(test)]
951mod tests {
952    use super::chars::*;
953    use super::*;
954    use alloc::collections::{LinkedList, VecDeque};
955    use alloc::{boxed::Box, format, string::ToString, vec, vec::Vec};
956
957    // Set up some aliases
958    #[allow(non_camel_case_types)]
959    type abc = (a, b, c);
960    #[allow(non_camel_case_types)]
961    type name = (n, a, m, e);
962    #[allow(non_camel_case_types)]
963    type age = (a, g, e);
964    #[allow(non_camel_case_types)]
965    type is_admin = (i, s, __, a, d, m, i, n);
966    #[allow(non_camel_case_types)]
967    type inner = (i, n, n, e, r);
968
969    #[test]
970    fn test_label_new_building() {
971        let l1 = field!(abc, 3);
972        assert_eq!(l1.value, 3);
973        assert_eq!(l1.name, "abc");
974        let l2 = field!((a, b, c), 3);
975        assert_eq!(l2.value, 3);
976        assert_eq!(l2.name, "abc");
977
978        // test named
979        let l3 = field!(abc, 3, "nope");
980        assert_eq!(l3.value, 3);
981        assert_eq!(l3.name, "nope");
982        let l4 = field!((a, b, c), 3, "nope");
983        assert_eq!(l4.value, 3);
984        assert_eq!(l4.name, "nope");
985    }
986
987    #[test]
988    fn test_field_construction() {
989        let f1 = field!(age, 3);
990        let f2 = field!((a, g, e), 3);
991        assert_eq!(f1, f2)
992    }
993
994    #[test]
995    fn test_field_debug() {
996        let field = field!(age, 3);
997        let hlist_pat![value_field] = hlist![field].into_value_labelled();
998
999        // names don't have quotation marks
1000        assert!(format!("{:?}", field).contains("name: age"));
1001        assert!(format!("{:?}", value_field).contains("name: age"));
1002        // :#? works
1003        assert!(format!("{:#?}", field).contains('\n'));
1004        assert!(format!("{:#?}", value_field).contains('\n'));
1005    }
1006
1007    #[test]
1008    fn test_anonymous_record_usage() {
1009        let record = hlist![field!(name, "Joe"), field!((a, g, e), 30)];
1010        let (name, _): (Field<name, _>, _) = record.pluck();
1011        assert_eq!(name.value, "Joe")
1012    }
1013
1014    #[test]
1015    fn test_pluck_by_name() {
1016        let record = hlist![
1017            field!(is_admin, true),
1018            field!(name, "Joe".to_string()),
1019            field!((a, g, e), 30),
1020        ];
1021
1022        let (name, r): (Field<name, _>, _) = record.clone().pluck_by_name();
1023        assert_eq!(name.value, "Joe");
1024        assert_eq!(r, hlist![field!(is_admin, true), field!((a, g, e), 30),]);
1025    }
1026
1027    #[test]
1028    fn test_ref_pluck_by_name() {
1029        let record = &hlist![
1030            field!(is_admin, true),
1031            field!(name, "Joe".to_string()),
1032            field!((a, g, e), 30),
1033        ];
1034
1035        let (name, r): (Field<name, _>, _) = record.pluck_by_name();
1036        assert_eq!(name.value, "Joe");
1037        assert_eq!(r, hlist![&field!(is_admin, true), &field!((a, g, e), 30),]);
1038    }
1039
1040    #[test]
1041    fn test_unlabelling() {
1042        let labelled_hlist = hlist![field!(name, "joe"), field!((a, g, e), 3)];
1043        let unlabelled = labelled_hlist.into_unlabelled();
1044        assert_eq!(unlabelled, hlist!["joe", 3])
1045    }
1046
1047    #[test]
1048    fn test_value_labelling() {
1049        let labelled_hlist = hlist![field!(name, "joe"), field!((a, g, e), 3)];
1050        let value_labelled: HList![ValueField<&str>, ValueField<isize>] =
1051            labelled_hlist.into_value_labelled();
1052        let hlist_pat!(f1, f2) = value_labelled;
1053        assert_eq!(f1.name, "name");
1054        assert_eq!(f2.name, "age");
1055    }
1056
1057    #[test]
1058    fn test_name() {
1059        let labelled = field!(name, "joe");
1060        assert_eq!(labelled.name, "name")
1061    }
1062
1063    #[test]
1064    fn test_transmogrify_hnil_identity() {
1065        let hnil_again: HNil = HNil.transmogrify();
1066        assert_eq!(HNil, hnil_again);
1067    }
1068
1069    #[test]
1070    fn test_transmogrify_hcons_sculpting_super_simple() {
1071        type Source = HList![Field<name, &'static str>, Field<age, i32>, Field<is_admin, bool>];
1072        type Target = HList![Field<age, i32>];
1073        let hcons: Source = hlist!(field!(name, "joe"), field!(age, 3), field!(is_admin, true));
1074        let t_hcons: Target = hcons.transmogrify();
1075        assert_eq!(t_hcons, hlist!(field!(age, 3)));
1076    }
1077
1078    #[test]
1079    fn test_transmogrify_hcons_sculpting_somewhat_simple() {
1080        type Source = HList![Field<name, &'static str>, Field<age, i32>, Field<is_admin, bool>];
1081        type Target = HList![Field<is_admin, bool>, Field<name, &'static str>];
1082        let hcons: Source = hlist!(field!(name, "joe"), field!(age, 3), field!(is_admin, true));
1083        let t_hcons: Target = hcons.transmogrify();
1084        assert_eq!(t_hcons, hlist!(field!(is_admin, true), field!(name, "joe")));
1085    }
1086
1087    #[test]
1088    fn test_transmogrify_hcons_recursive_simple() {
1089        type Source = HList![
1090            Field<name,  HList![
1091                Field<inner, f32>,
1092                Field<is_admin, bool>,
1093            ]>,
1094            Field<age, i32>,
1095            Field<is_admin, bool>];
1096        type Target = HList![
1097            Field<is_admin, bool>,
1098            Field<name,  HList![
1099                Field<is_admin, bool>,
1100            ]>,
1101        ];
1102        let source: Source = hlist![
1103            field!(name, hlist![field!(inner, 42f32), field!(is_admin, true)]),
1104            field!(age, 32),
1105            field!(is_admin, true)
1106        ];
1107        let target: Target = source.transmogrify();
1108        assert_eq!(
1109            target,
1110            hlist![
1111                field!(is_admin, true),
1112                field!(name, hlist![field!(is_admin, true)]),
1113            ]
1114        )
1115    }
1116
1117    #[test]
1118    fn test_transmogrify_hcons_sculpting_required_simple() {
1119        type Source = HList![Field<name, &'static str>, Field<age, i32>, Field<is_admin, bool>];
1120        type Target = HList![Field<is_admin, bool>, Field<name, &'static str>, Field<age, i32>];
1121        let hcons: Source = hlist!(field!(name, "joe"), field!(age, 3), field!(is_admin, true));
1122        let t_hcons: Target = hcons.transmogrify();
1123        assert_eq!(
1124            t_hcons,
1125            hlist!(field!(is_admin, true), field!(name, "joe"), field!(age, 3))
1126        );
1127    }
1128
1129    #[test]
1130    fn test_transmogrify_identical_transform_labelled_fields() {
1131        type Source = HList![
1132            Field<name,  &'static str>,
1133            Field<age, i32>,
1134            Field<is_admin, bool>
1135        ];
1136        type Target = Source;
1137        let source: Source = hlist![field!(name, "joe"), field!(age, 32), field!(is_admin, true)];
1138        let target: Target = source.transmogrify();
1139        assert_eq!(
1140            target,
1141            hlist![field!(name, "joe"), field!(age, 32), field!(is_admin, true)]
1142        )
1143    }
1144
1145    #[test]
1146    fn test_transmogrify_through_containers() {
1147        type SourceOuter<T> = HList![
1148            Field<name, &'static str>,
1149            Field<inner, T>,
1150        ];
1151        type SourceInner = HList![
1152            Field<is_admin, bool>,
1153            Field<age, i32>,
1154        ];
1155        type TargetOuter<T> = HList![
1156            Field<name, &'static str>,
1157            Field<inner, T>,
1158        ];
1159        type TargetInner = HList![
1160            Field<age, i32>,
1161            Field<is_admin, bool>,
1162        ];
1163
1164        fn create_inner() -> (SourceInner, TargetInner) {
1165            let source_inner: SourceInner = hlist![field!(is_admin, true), field!(age, 14)];
1166            let target_inner: TargetInner = hlist![field!(age, 14), field!(is_admin, true)];
1167            (source_inner, target_inner)
1168        }
1169
1170        // Vec -> Vec
1171        let (source_inner, target_inner) = create_inner();
1172        let source: SourceOuter<Vec<SourceInner>> =
1173            hlist![field!(name, "Joe"), field!(inner, vec![source_inner])];
1174        let target: TargetOuter<Vec<TargetInner>> = source.transmogrify();
1175        assert_eq!(
1176            target,
1177            hlist![field!(name, "Joe"), field!(inner, vec![target_inner])]
1178        );
1179
1180        // LInkedList -> LinkedList
1181        let (source_inner, target_inner) = create_inner();
1182        let source_inner = {
1183            let mut list = LinkedList::new();
1184            list.push_front(source_inner);
1185            list
1186        };
1187        let target_inner = {
1188            let mut list = LinkedList::new();
1189            list.push_front(target_inner);
1190            list
1191        };
1192        let source: SourceOuter<LinkedList<SourceInner>> =
1193            hlist![field!(name, "Joe"), field!(inner, source_inner)];
1194        let target: TargetOuter<LinkedList<TargetInner>> = source.transmogrify();
1195        assert_eq!(
1196            target,
1197            hlist![field!(name, "Joe"), field!(inner, target_inner)]
1198        );
1199
1200        // VecDeque -> VecDeque
1201        let (source_inner, target_inner) = create_inner();
1202        let source_inner = {
1203            let mut list = VecDeque::new();
1204            list.push_front(source_inner);
1205            list
1206        };
1207        let target_inner = {
1208            let mut list = VecDeque::new();
1209            list.push_front(target_inner);
1210            list
1211        };
1212        let source: SourceOuter<VecDeque<SourceInner>> =
1213            hlist![field!(name, "Joe"), field!(inner, source_inner)];
1214        let target: TargetOuter<VecDeque<TargetInner>> = source.transmogrify();
1215        assert_eq!(
1216            target,
1217            hlist![field!(name, "Joe"), field!(inner, target_inner)]
1218        );
1219
1220        // Option -> Option
1221        let (source_inner, target_inner) = create_inner();
1222        let source_inner = Some(source_inner);
1223        let target_inner = Some(target_inner);
1224        let source: SourceOuter<Option<SourceInner>> =
1225            hlist![field!(name, "Joe"), field!(inner, source_inner)];
1226        let target: TargetOuter<Option<TargetInner>> = source.transmogrify();
1227        assert_eq!(
1228            target,
1229            hlist![field!(name, "Joe"), field!(inner, target_inner)]
1230        );
1231        let source: SourceOuter<Option<SourceInner>> =
1232            hlist![field!(name, "Joe"), field!(inner, None)];
1233        let target: TargetOuter<Option<TargetInner>> = source.transmogrify();
1234        assert_eq!(target, hlist![field!(name, "Joe"), field!(inner, None)]);
1235
1236        // Box -> Box
1237        let (source_inner, target_inner) = create_inner();
1238        let source_inner = Box::new(source_inner);
1239        let target_inner = Box::new(target_inner);
1240        let source: SourceOuter<Box<SourceInner>> =
1241            hlist![field!(name, "Joe"), field!(inner, source_inner)];
1242        let target: TargetOuter<Box<TargetInner>> = source.transmogrify();
1243        assert_eq!(
1244            target,
1245            hlist![field!(name, "Joe"), field!(inner, target_inner)]
1246        );
1247    }
1248
1249    //    #[test]
1250    //    fn test_transmogrify_identical_transform_nested_labelled_fields() {
1251    //        type Source = HList![
1252    //    Field<name,  HList![
1253    //        Field<inner, f32>,
1254    //        Field<is_admin, bool>,
1255    //    ]>,
1256    //    Field<age, i32>,
1257    //    Field<is_admin, bool>];
1258    //        type Target = Source;
1259    //        let source: Source = hlist![
1260    //            field!(name, hlist![field!(inner, 42f32), field!(is_admin, true)]),
1261    //            field!(age, 32),
1262    //            field!(is_admin, true)
1263    //        ];
1264    //        let target: Target = source.transmogrify();
1265    //        assert_eq!(
1266    //            target,
1267    //            hlist![
1268    //                field!(name, hlist![field!(inner, 42f32), field!(is_admin, true)]),
1269    //                field!(age, 32),
1270    //                field!(is_admin, true)
1271    //            ]
1272    //        )
1273    //    }
1274}