use crate::hlist::*;
use crate::indices::*;
use crate::traits::ToRef;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use core::fmt;
use core::marker::PhantomData;
pub trait LabelledGeneric {
type Repr;
fn into(self) -> Self::Repr;
fn from(repr: Self::Repr) -> Self;
#[inline(always)]
fn convert_from<Src>(src: Src) -> Self
where
Src: LabelledGeneric<Repr = Self::Repr>,
Self: Sized,
{
let repr = <Src as LabelledGeneric>::into(src);
<Self as LabelledGeneric>::from(repr)
}
#[deprecated(note = "obsolete, transform_from instead")]
fn sculpted_convert_from<A, Indices>(a: A) -> Self
where
A: LabelledGeneric,
Self: Sized,
<A as LabelledGeneric>::Repr: Sculptor<<Self as LabelledGeneric>::Repr, Indices>,
{
<Self as LabelledGeneric>::transform_from(a)
}
#[inline(always)]
fn transform_from<Src, Indices>(src: Src) -> Self
where
Src: LabelledGeneric,
Self: Sized,
<Src as LabelledGeneric>::Repr: Sculptor<<Self as LabelledGeneric>::Repr, Indices>,
{
let src_gen = <Src as LabelledGeneric>::into(src);
let (self_gen, _): (<Self as LabelledGeneric>::Repr, _) = src_gen.sculpt();
<Self as LabelledGeneric>::from(self_gen)
}
}
pub trait IntoLabelledGeneric {
type Repr;
fn into(self) -> Self::Repr;
}
impl<A> IntoLabelledGeneric for A
where
A: LabelledGeneric,
{
type Repr = <A as LabelledGeneric>::Repr;
#[inline(always)]
fn into(self) -> <Self as IntoLabelledGeneric>::Repr {
self.into()
}
}
pub fn from_labelled_generic<Dst, Repr>(repr: Repr) -> Dst
where
Dst: LabelledGeneric<Repr = Repr>,
{
<Dst as LabelledGeneric>::from(repr)
}
pub fn into_labelled_generic<Src, Repr>(src: Src) -> Repr
where
Src: LabelledGeneric<Repr = Repr>,
{
<Src as LabelledGeneric>::into(src)
}
pub fn labelled_convert_from<Src, Dst, Repr>(src: Src) -> Dst
where
Src: LabelledGeneric<Repr = Repr>,
Dst: LabelledGeneric<Repr = Repr>,
{
<Dst as LabelledGeneric>::convert_from(src)
}
#[deprecated(note = "obsolete, transform_from instead")]
pub fn sculpted_convert_from<A, B, Indices>(a: A) -> B
where
A: LabelledGeneric,
B: LabelledGeneric,
<A as LabelledGeneric>::Repr: Sculptor<<B as LabelledGeneric>::Repr, Indices>,
{
<B as LabelledGeneric>::transform_from(a)
}
pub fn transform_from<Src, Dst, Indices>(src: Src) -> Dst
where
Src: LabelledGeneric,
Dst: LabelledGeneric,
<Src as LabelledGeneric>::Repr: Sculptor<<Dst as LabelledGeneric>::Repr, Indices>,
{
<Dst as LabelledGeneric>::transform_from(src)
}
pub mod chars {
macro_rules! create_enums_for {
($($i: ident)*) => {
$(
#[allow(non_snake_case, non_camel_case_types)]
#[derive(PartialEq, Debug, Eq, Clone, Copy, PartialOrd, Ord, Hash)]
pub enum $i {}
)*
}
}
create_enums_for! {
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
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
_1 _2 _3 _4 _5 _6 _7 _8 _9 _0 __ _uc uc_
}
#[test]
fn simple_var_names_are_allowed() {
let a = 3;
#[allow(clippy::match_single_binding)]
match a {
a => assert_eq!(a, 3),
}
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)]
pub struct Field<Name, Type> {
name_type_holder: PhantomData<Name>,
pub name: &'static str,
pub value: Type,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)]
pub struct ValueField<Type> {
pub name: &'static str,
pub value: Type,
}
impl<Name, Type> fmt::Debug for Field<Name, Type>
where
Type: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Field")
.field("name", &DebugAsDisplay(&self.name))
.field("value", &self.value)
.finish()
}
}
impl<Type> fmt::Debug for ValueField<Type>
where
Type: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ValueField")
.field("name", &DebugAsDisplay(&self.name))
.field("value", &self.value)
.finish()
}
}
struct DebugAsDisplay<T>(T);
impl<T: fmt::Display> fmt::Debug for DebugAsDisplay<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
pub fn field_with_name<Label, Value>(name: &'static str, value: Value) -> Field<Label, Value> {
Field {
name_type_holder: PhantomData,
name,
value,
}
}
pub trait IntoUnlabelled {
type Output;
fn into_unlabelled(self) -> Self::Output;
}
impl IntoUnlabelled for HNil {
type Output = HNil;
fn into_unlabelled(self) -> Self::Output {
self
}
}
impl<Label, Value, Tail> IntoUnlabelled for HCons<Field<Label, Value>, Tail>
where
Tail: IntoUnlabelled,
{
type Output = HCons<Value, <Tail as IntoUnlabelled>::Output>;
fn into_unlabelled(self) -> Self::Output {
HCons {
head: self.head.value,
tail: self.tail.into_unlabelled(),
}
}
}
pub trait IntoValueLabelled {
type Output;
fn into_value_labelled(self) -> Self::Output;
}
impl IntoValueLabelled for HNil {
type Output = HNil;
fn into_value_labelled(self) -> Self::Output {
self
}
}
impl<Label, Value, Tail> IntoValueLabelled for HCons<Field<Label, Value>, Tail>
where
Tail: IntoValueLabelled,
{
type Output = HCons<ValueField<Value>, <Tail as IntoValueLabelled>::Output>;
fn into_value_labelled(self) -> Self::Output {
HCons {
head: ValueField {
name: self.head.name,
value: self.head.value,
},
tail: self.tail.into_value_labelled(),
}
}
}
pub trait ByNameFieldPlucker<TargetKey, Index> {
type TargetValue;
type Remainder;
fn pluck_by_name(self) -> (Field<TargetKey, Self::TargetValue>, Self::Remainder);
}
impl<K, V, Tail> ByNameFieldPlucker<K, Here> for HCons<Field<K, V>, Tail> {
type TargetValue = V;
type Remainder = Tail;
#[inline(always)]
fn pluck_by_name(self) -> (Field<K, Self::TargetValue>, Self::Remainder) {
let field = field_with_name(self.head.name, self.head.value);
(field, self.tail)
}
}
impl<Head, Tail, K, TailIndex> ByNameFieldPlucker<K, There<TailIndex>> for HCons<Head, Tail>
where
Tail: ByNameFieldPlucker<K, TailIndex>,
{
type TargetValue = <Tail as ByNameFieldPlucker<K, TailIndex>>::TargetValue;
type Remainder = HCons<Head, <Tail as ByNameFieldPlucker<K, TailIndex>>::Remainder>;
#[inline(always)]
fn pluck_by_name(self) -> (Field<K, Self::TargetValue>, Self::Remainder) {
let (target, tail_remainder) =
<Tail as ByNameFieldPlucker<K, TailIndex>>::pluck_by_name(self.tail);
(
target,
HCons {
head: self.head,
tail: tail_remainder,
},
)
}
}
impl<'a, K, V, Tail: ToRef<'a>> ByNameFieldPlucker<K, Here> for &'a HCons<Field<K, V>, Tail> {
type TargetValue = &'a V;
type Remainder = <Tail as ToRef<'a>>::Output;
#[inline(always)]
fn pluck_by_name(self) -> (Field<K, Self::TargetValue>, Self::Remainder) {
let field = field_with_name(self.head.name, &self.head.value);
(field, self.tail.to_ref())
}
}
impl<'a, Head, Tail, K, TailIndex> ByNameFieldPlucker<K, There<TailIndex>> for &'a HCons<Head, Tail>
where
&'a Tail: ByNameFieldPlucker<K, TailIndex>,
{
type TargetValue = <&'a Tail as ByNameFieldPlucker<K, TailIndex>>::TargetValue;
type Remainder = HCons<&'a Head, <&'a Tail as ByNameFieldPlucker<K, TailIndex>>::Remainder>;
#[inline(always)]
fn pluck_by_name(self) -> (Field<K, Self::TargetValue>, Self::Remainder) {
let (target, tail_remainder) =
<&'a Tail as ByNameFieldPlucker<K, TailIndex>>::pluck_by_name(&self.tail);
(
target,
HCons {
head: &self.head,
tail: tail_remainder,
},
)
}
}
pub trait Transmogrifier<Target, TransmogrifyIndexIndices> {
fn transmogrify(self) -> Target;
}
impl<Key, SourceValue> Transmogrifier<SourceValue, IdentityTransMog> for Field<Key, SourceValue> {
#[inline(always)]
fn transmogrify(self) -> SourceValue {
self.value
}
}
#[cfg(feature = "alloc")]
mod _alloc {
use super::MappingIndicesWrapper;
use super::{Field, Transmogrifier};
use alloc::boxed::Box;
use alloc::collections::{LinkedList, VecDeque};
use alloc::vec::Vec;
macro_rules! transmogrify_seq {
($container:ident) => {
impl<Key, Source, Target, InnerIndices>
Transmogrifier<$container<Target>, MappingIndicesWrapper<InnerIndices>>
for Field<Key, $container<Source>>
where
Source: Transmogrifier<Target, InnerIndices>,
{
fn transmogrify(self) -> $container<Target> {
self.value.into_iter().map(|e| e.transmogrify()).collect()
}
}
};
}
transmogrify_seq!(Vec);
transmogrify_seq!(LinkedList);
transmogrify_seq!(VecDeque);
impl<Key, Source, Target, InnerIndices>
Transmogrifier<Box<Target>, MappingIndicesWrapper<InnerIndices>> for Field<Key, Box<Source>>
where
Source: Transmogrifier<Target, InnerIndices>,
{
fn transmogrify(self) -> Box<Target> {
Box::new(self.value.transmogrify())
}
}
}
impl<Key, Source, Target, InnerIndices>
Transmogrifier<Option<Target>, MappingIndicesWrapper<InnerIndices>>
for Field<Key, Option<Source>>
where
Source: Transmogrifier<Target, InnerIndices>,
{
fn transmogrify(self) -> Option<Target> {
self.value.map(|e| e.transmogrify())
}
}
impl Transmogrifier<HNil, HNil> for HNil {
#[inline(always)]
fn transmogrify(self) -> HNil {
HNil
}
}
impl<SourceHead, SourceTail> Transmogrifier<HNil, HNil> for HCons<SourceHead, SourceTail> {
#[inline(always)]
fn transmogrify(self) -> HNil {
HNil
}
}
impl<
SourceHead,
SourceTail,
TargetName,
TargetHead,
TargetTail,
TransmogHeadIndex,
TransmogTailIndices,
> Transmogrifier<HCons<TargetHead, TargetTail>, HCons<TransmogHeadIndex, TransmogTailIndices>>
for Field<TargetName, HCons<SourceHead, SourceTail>>
where
HCons<SourceHead, SourceTail>: Transmogrifier<
HCons<TargetHead, TargetTail>,
HCons<TransmogHeadIndex, TransmogTailIndices>,
>,
{
#[inline(always)]
fn transmogrify(self) -> HCons<TargetHead, TargetTail> {
self.value.transmogrify()
}
}
impl<
SourceHead,
SourceTail,
TargetHeadName,
TargetHeadValue,
TargetTail,
PluckSourceHeadNameIndex,
TransMogSourceHeadValueIndices,
TransMogTailIndices,
>
Transmogrifier<
HCons<Field<TargetHeadName, TargetHeadValue>, TargetTail>,
HCons<
DoTransmog<PluckSourceHeadNameIndex, TransMogSourceHeadValueIndices>,
TransMogTailIndices,
>,
> for HCons<SourceHead, SourceTail>
where
HCons<SourceHead, SourceTail>: ByNameFieldPlucker<TargetHeadName, PluckSourceHeadNameIndex>,
Field<
TargetHeadName,
<HCons<SourceHead, SourceTail> as ByNameFieldPlucker<
TargetHeadName,
PluckSourceHeadNameIndex,
>>::TargetValue,
>: Transmogrifier<TargetHeadValue, TransMogSourceHeadValueIndices>,
<HCons<SourceHead, SourceTail> as ByNameFieldPlucker<
TargetHeadName,
PluckSourceHeadNameIndex,
>>::Remainder: Transmogrifier<TargetTail, TransMogTailIndices>,
{
#[inline(always)]
fn transmogrify(self) -> HCons<Field<TargetHeadName, TargetHeadValue>, TargetTail> {
let (source_field_for_head_target_name, remainder) = self.pluck_by_name();
let name = source_field_for_head_target_name.name;
let transmogrified_value: TargetHeadValue =
source_field_for_head_target_name.transmogrify();
let as_field: Field<TargetHeadName, TargetHeadValue> =
field_with_name(name, transmogrified_value);
HCons {
head: as_field,
tail: remainder.transmogrify(),
}
}
}
impl<Source, Target, TransmogIndices>
Transmogrifier<Target, LabelledGenericTransmogIndicesWrapper<TransmogIndices>> for Source
where
Source: LabelledGeneric,
Target: LabelledGeneric,
<Source as LabelledGeneric>::Repr:
Transmogrifier<<Target as LabelledGeneric>::Repr, TransmogIndices>,
{
#[inline(always)]
fn transmogrify(self) -> Target {
let source_as_repr = self.into();
let source_transmogged = source_as_repr.transmogrify();
<Target as LabelledGeneric>::from(source_transmogged)
}
}
impl<Source, TargetName, TargetValue, TransmogIndices>
Transmogrifier<TargetValue, PluckedLabelledGenericIndicesWrapper<TransmogIndices>>
for Field<TargetName, Source>
where
Source: LabelledGeneric,
TargetValue: LabelledGeneric,
Source: Transmogrifier<TargetValue, TransmogIndices>,
{
#[inline(always)]
fn transmogrify(self) -> TargetValue {
self.value.transmogrify()
}
}
#[cfg(test)]
mod tests {
use super::chars::*;
use super::*;
use alloc::collections::{LinkedList, VecDeque};
use alloc::{boxed::Box, format, string::ToString, vec, vec::Vec};
#[allow(non_camel_case_types)]
type abc = (a, b, c);
#[allow(non_camel_case_types)]
type name = (n, a, m, e);
#[allow(non_camel_case_types)]
type age = (a, g, e);
#[allow(non_camel_case_types)]
type is_admin = (i, s, __, a, d, m, i, n);
#[allow(non_camel_case_types)]
type inner = (i, n, n, e, r);
#[test]
fn test_label_new_building() {
let l1 = field!(abc, 3);
assert_eq!(l1.value, 3);
assert_eq!(l1.name, "abc");
let l2 = field!((a, b, c), 3);
assert_eq!(l2.value, 3);
assert_eq!(l2.name, "abc");
let l3 = field!(abc, 3, "nope");
assert_eq!(l3.value, 3);
assert_eq!(l3.name, "nope");
let l4 = field!((a, b, c), 3, "nope");
assert_eq!(l4.value, 3);
assert_eq!(l4.name, "nope");
}
#[test]
fn test_field_construction() {
let f1 = field!(age, 3);
let f2 = field!((a, g, e), 3);
assert_eq!(f1, f2)
}
#[test]
fn test_field_debug() {
let field = field!(age, 3);
let hlist_pat![value_field] = hlist![field].into_value_labelled();
assert!(format!("{:?}", field).contains("name: age"));
assert!(format!("{:?}", value_field).contains("name: age"));
assert!(format!("{:#?}", field).contains('\n'));
assert!(format!("{:#?}", value_field).contains('\n'));
}
#[test]
fn test_anonymous_record_usage() {
let record = hlist![field!(name, "Joe"), field!((a, g, e), 30)];
let (name, _): (Field<name, _>, _) = record.pluck();
assert_eq!(name.value, "Joe")
}
#[test]
fn test_pluck_by_name() {
let record = hlist![
field!(is_admin, true),
field!(name, "Joe".to_string()),
field!((a, g, e), 30),
];
let (name, r): (Field<name, _>, _) = record.clone().pluck_by_name();
assert_eq!(name.value, "Joe");
assert_eq!(r, hlist![field!(is_admin, true), field!((a, g, e), 30),]);
}
#[test]
fn test_ref_pluck_by_name() {
let record = &hlist![
field!(is_admin, true),
field!(name, "Joe".to_string()),
field!((a, g, e), 30),
];
let (name, r): (Field<name, _>, _) = record.pluck_by_name();
assert_eq!(name.value, "Joe");
assert_eq!(r, hlist![&field!(is_admin, true), &field!((a, g, e), 30),]);
}
#[test]
fn test_unlabelling() {
let labelled_hlist = hlist![field!(name, "joe"), field!((a, g, e), 3)];
let unlabelled = labelled_hlist.into_unlabelled();
assert_eq!(unlabelled, hlist!["joe", 3])
}
#[test]
fn test_value_labelling() {
let labelled_hlist = hlist![field!(name, "joe"), field!((a, g, e), 3)];
let value_labelled: HList![ValueField<&str>, ValueField<isize>] =
labelled_hlist.into_value_labelled();
let hlist_pat!(f1, f2) = value_labelled;
assert_eq!(f1.name, "name");
assert_eq!(f2.name, "age");
}
#[test]
fn test_name() {
let labelled = field!(name, "joe");
assert_eq!(labelled.name, "name")
}
#[test]
fn test_transmogrify_hnil_identity() {
let hnil_again: HNil = HNil.transmogrify();
assert_eq!(HNil, hnil_again);
}
#[test]
fn test_transmogrify_hcons_sculpting_super_simple() {
type Source = HList![Field<name, &'static str>, Field<age, i32>, Field<is_admin, bool>];
type Target = HList![Field<age, i32>];
let hcons: Source = hlist!(field!(name, "joe"), field!(age, 3), field!(is_admin, true));
let t_hcons: Target = hcons.transmogrify();
assert_eq!(t_hcons, hlist!(field!(age, 3)));
}
#[test]
fn test_transmogrify_hcons_sculpting_somewhat_simple() {
type Source = HList![Field<name, &'static str>, Field<age, i32>, Field<is_admin, bool>];
type Target = HList![Field<is_admin, bool>, Field<name, &'static str>];
let hcons: Source = hlist!(field!(name, "joe"), field!(age, 3), field!(is_admin, true));
let t_hcons: Target = hcons.transmogrify();
assert_eq!(t_hcons, hlist!(field!(is_admin, true), field!(name, "joe")));
}
#[test]
fn test_transmogrify_hcons_recursive_simple() {
type Source = HList![
Field<name, HList![
Field<inner, f32>,
Field<is_admin, bool>,
]>,
Field<age, i32>,
Field<is_admin, bool>];
type Target = HList![
Field<is_admin, bool>,
Field<name, HList![
Field<is_admin, bool>,
]>,
];
let source: Source = hlist![
field!(name, hlist![field!(inner, 42f32), field!(is_admin, true)]),
field!(age, 32),
field!(is_admin, true)
];
let target: Target = source.transmogrify();
assert_eq!(
target,
hlist![
field!(is_admin, true),
field!(name, hlist![field!(is_admin, true)]),
]
)
}
#[test]
fn test_transmogrify_hcons_sculpting_required_simple() {
type Source = HList![Field<name, &'static str>, Field<age, i32>, Field<is_admin, bool>];
type Target = HList![Field<is_admin, bool>, Field<name, &'static str>, Field<age, i32>];
let hcons: Source = hlist!(field!(name, "joe"), field!(age, 3), field!(is_admin, true));
let t_hcons: Target = hcons.transmogrify();
assert_eq!(
t_hcons,
hlist!(field!(is_admin, true), field!(name, "joe"), field!(age, 3))
);
}
#[test]
fn test_transmogrify_identical_transform_labelled_fields() {
type Source = HList![
Field<name, &'static str>,
Field<age, i32>,
Field<is_admin, bool>
];
type Target = Source;
let source: Source = hlist![field!(name, "joe"), field!(age, 32), field!(is_admin, true)];
let target: Target = source.transmogrify();
assert_eq!(
target,
hlist![field!(name, "joe"), field!(age, 32), field!(is_admin, true)]
)
}
#[test]
fn test_transmogrify_through_containers() {
type SourceOuter<T> = HList![
Field<name, &'static str>,
Field<inner, T>,
];
type SourceInner = HList![
Field<is_admin, bool>,
Field<age, i32>,
];
type TargetOuter<T> = HList![
Field<name, &'static str>,
Field<inner, T>,
];
type TargetInner = HList![
Field<age, i32>,
Field<is_admin, bool>,
];
fn create_inner() -> (SourceInner, TargetInner) {
let source_inner: SourceInner = hlist![field!(is_admin, true), field!(age, 14)];
let target_inner: TargetInner = hlist![field!(age, 14), field!(is_admin, true)];
(source_inner, target_inner)
}
let (source_inner, target_inner) = create_inner();
let source: SourceOuter<Vec<SourceInner>> =
hlist![field!(name, "Joe"), field!(inner, vec![source_inner])];
let target: TargetOuter<Vec<TargetInner>> = source.transmogrify();
assert_eq!(
target,
hlist![field!(name, "Joe"), field!(inner, vec![target_inner])]
);
let (source_inner, target_inner) = create_inner();
let source_inner = {
let mut list = LinkedList::new();
list.push_front(source_inner);
list
};
let target_inner = {
let mut list = LinkedList::new();
list.push_front(target_inner);
list
};
let source: SourceOuter<LinkedList<SourceInner>> =
hlist![field!(name, "Joe"), field!(inner, source_inner)];
let target: TargetOuter<LinkedList<TargetInner>> = source.transmogrify();
assert_eq!(
target,
hlist![field!(name, "Joe"), field!(inner, target_inner)]
);
let (source_inner, target_inner) = create_inner();
let source_inner = {
let mut list = VecDeque::new();
list.push_front(source_inner);
list
};
let target_inner = {
let mut list = VecDeque::new();
list.push_front(target_inner);
list
};
let source: SourceOuter<VecDeque<SourceInner>> =
hlist![field!(name, "Joe"), field!(inner, source_inner)];
let target: TargetOuter<VecDeque<TargetInner>> = source.transmogrify();
assert_eq!(
target,
hlist![field!(name, "Joe"), field!(inner, target_inner)]
);
let (source_inner, target_inner) = create_inner();
let source_inner = Some(source_inner);
let target_inner = Some(target_inner);
let source: SourceOuter<Option<SourceInner>> =
hlist![field!(name, "Joe"), field!(inner, source_inner)];
let target: TargetOuter<Option<TargetInner>> = source.transmogrify();
assert_eq!(
target,
hlist![field!(name, "Joe"), field!(inner, target_inner)]
);
let source: SourceOuter<Option<SourceInner>> =
hlist![field!(name, "Joe"), field!(inner, None)];
let target: TargetOuter<Option<TargetInner>> = source.transmogrify();
assert_eq!(target, hlist![field!(name, "Joe"), field!(inner, None)]);
let (source_inner, target_inner) = create_inner();
let source_inner = Box::new(source_inner);
let target_inner = Box::new(target_inner);
let source: SourceOuter<Box<SourceInner>> =
hlist![field!(name, "Joe"), field!(inner, source_inner)];
let target: TargetOuter<Box<TargetInner>> = source.transmogrify();
assert_eq!(
target,
hlist![field!(name, "Joe"), field!(inner, target_inner)]
);
}
}