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