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