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}