use crate::hlist::{Coniunctio, Nihil, Sculptor};
use crate::indices::{
DoTransfig, Here, IdentityTransfig, MappingIndicesWrapper,
NominataUniversalisTransfigIndicesWrapper, PluckedNominataUniversalisIndicesWrapper, There,
};
use crate::traits::ToRef;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use core::fmt;
use core::marker::PhantomData;
pub trait NominataUniversalis {
type Repr;
fn into(self) -> Self::Repr;
fn from(repr: Self::Repr) -> Self;
#[inline(always)]
fn convert_from<Src>(src: Src) -> Self
where
Src: NominataUniversalis<Repr = Self::Repr>,
Self: Sized,
{
let repr = <Src as NominataUniversalis>::into(src);
<Self as NominataUniversalis>::from(repr)
}
#[inline(always)]
fn transform_from<Src, Indices>(src: Src) -> Self
where
Src: NominataUniversalis,
Self: Sized,
<Src as NominataUniversalis>::Repr: Sculptor<<Self as NominataUniversalis>::Repr, Indices>,
{
let src_gen = <Src as NominataUniversalis>::into(src);
let (self_gen, _): (<Self as NominataUniversalis>::Repr, _) = src_gen.sculpt();
<Self as NominataUniversalis>::from(self_gen)
}
}
pub trait IntoNominataUniversalis {
type Repr;
fn into(self) -> Self::Repr;
}
impl<A> IntoNominataUniversalis for A
where
A: NominataUniversalis,
{
type Repr = <A as NominataUniversalis>::Repr;
#[inline(always)]
fn into(self) -> <Self as IntoNominataUniversalis>::Repr {
self.into()
}
}
#[inline]
pub fn from_labelled_universalis<Dst, Repr>(repr: Repr) -> Dst
where
Dst: NominataUniversalis<Repr = Repr>,
{
<Dst as NominataUniversalis>::from(repr)
}
#[inline]
pub fn into_labelled_universalis<Src, Repr>(src: Src) -> Repr
where
Src: NominataUniversalis<Repr = Repr>,
{
<Src as NominataUniversalis>::into(src)
}
#[inline]
pub fn nominata_convert_from<Src, Dst, Repr>(src: Src) -> Dst
where
Src: NominataUniversalis<Repr = Repr>,
Dst: NominataUniversalis<Repr = Repr>,
{
<Dst as NominataUniversalis>::convert_from(src)
}
#[inline]
pub fn transform_from<Src, Dst, Indices>(src: Src) -> Dst
where
Src: NominataUniversalis,
Dst: NominataUniversalis,
<Src as NominataUniversalis>::Repr: Sculptor<<Dst as NominataUniversalis>::Repr, Indices>,
{
<Dst as NominataUniversalis>::transform_from(src)
}
pub mod chars {
macro_rules! create_char_types {
($($i:ident)*) => {
$(
#[doc = concat!("Type-level character `", stringify!($i), "`.")]
#[doc = ""]
#[doc = "Uninhabited: it exists only to spell out labels in the"]
#[doc = "type system and is never constructed at runtime."]
#[derive(PartialEq, Debug, Eq, Clone, Copy, PartialOrd, Ord, Hash)]
pub enum $i {}
)*
};
}
create_char_types! {
La Lb Lc Ld Le Lf Lg Lh Li Lj Lk Ll Lm Ln Lo Lp Lq Lr Ls Lt Lu Lv Lw Lx Ly Lz
Ua Ub Uc Ud Ue Uf Ug Uh Ui Uj Uk Ul Um Un Uo Up Uq Ur Us Ut Uu Uv Uw Ux Uy Uz
}
create_char_types! {
N0 N1 N2 N3 N4 N5 N6 N7 N8 N9
}
create_char_types! {
Underscore DoubleUnderscore UnderscoreUc UcUnderscore
}
create_char_types! {
F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23
}
#[test]
fn simple_var_names_are_allowed() {
let a = 3;
match a {
0 => unreachable!("a is 3"),
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)
}
}
#[inline]
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 Nihil {
type Output = Nihil;
#[inline]
fn into_unlabelled(self) -> Self::Output {
self
}
}
impl<Label, Value, Tail> IntoUnlabelled for Coniunctio<Field<Label, Value>, Tail>
where
Tail: IntoUnlabelled,
{
type Output = Coniunctio<Value, <Tail as IntoUnlabelled>::Output>;
#[inline]
fn into_unlabelled(self) -> Self::Output {
Coniunctio {
head: self.head.value,
tail: self.tail.into_unlabelled(),
}
}
}
pub trait IntoValueLabelled {
type Output;
fn into_value_labelled(self) -> Self::Output;
}
impl IntoValueLabelled for Nihil {
type Output = Nihil;
#[inline]
fn into_value_labelled(self) -> Self::Output {
self
}
}
impl<Label, Value, Tail> IntoValueLabelled for Coniunctio<Field<Label, Value>, Tail>
where
Tail: IntoValueLabelled,
{
type Output = Coniunctio<ValueField<Value>, <Tail as IntoValueLabelled>::Output>;
#[inline]
fn into_value_labelled(self) -> Self::Output {
Coniunctio {
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 Coniunctio<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 Coniunctio<Head, Tail>
where
Tail: ByNameFieldPlucker<K, TailIndex>,
{
type TargetValue = <Tail as ByNameFieldPlucker<K, TailIndex>>::TargetValue;
type Remainder = Coniunctio<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,
Coniunctio {
head: self.head,
tail: tail_remainder,
},
)
}
}
impl<'a, K, V, Tail: ToRef<'a>> ByNameFieldPlucker<K, Here> for &'a Coniunctio<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 Coniunctio<Head, Tail>
where
&'a Tail: ByNameFieldPlucker<K, TailIndex>,
{
type TargetValue = <&'a Tail as ByNameFieldPlucker<K, TailIndex>>::TargetValue;
type Remainder =
Coniunctio<&'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,
Coniunctio {
head: &self.head,
tail: tail_remainder,
},
)
}
}
pub trait Transfigurator<Target, TransfigureIndexIndices> {
fn transfigure(self) -> Target;
}
impl<Key, SourceValue> Transfigurator<SourceValue, IdentityTransfig> for Field<Key, SourceValue> {
#[inline(always)]
fn transfigure(self) -> SourceValue {
self.value
}
}
#[cfg(feature = "alloc")]
mod _alloc {
use super::MappingIndicesWrapper;
use super::{Field, Transfigurator};
use alloc::boxed::Box;
use alloc::collections::{LinkedList, VecDeque};
use alloc::vec::Vec;
macro_rules! transfigure_seq {
($container:ident) => {
impl<Key, Source, Target, InnerIndices>
Transfigurator<$container<Target>, MappingIndicesWrapper<InnerIndices>>
for Field<Key, $container<Source>>
where
Source: Transfigurator<Target, InnerIndices>,
{
#[inline]
fn transfigure(self) -> $container<Target> {
self.value.into_iter().map(|e| e.transfigure()).collect()
}
}
};
}
transfigure_seq!(Vec);
transfigure_seq!(LinkedList);
transfigure_seq!(VecDeque);
impl<Key, Source, Target, InnerIndices>
Transfigurator<Box<Target>, MappingIndicesWrapper<InnerIndices>> for Field<Key, Box<Source>>
where
Source: Transfigurator<Target, InnerIndices>,
{
#[inline]
fn transfigure(self) -> Box<Target> {
Box::new(self.value.transfigure())
}
}
}
impl<Key, Source, Target, InnerIndices>
Transfigurator<Option<Target>, MappingIndicesWrapper<InnerIndices>>
for Field<Key, Option<Source>>
where
Source: Transfigurator<Target, InnerIndices>,
{
#[inline]
fn transfigure(self) -> Option<Target> {
self.value.map(Transfigurator::transfigure)
}
}
impl Transfigurator<Nihil, Nihil> for Nihil {
#[inline(always)]
fn transfigure(self) -> Nihil {
Nihil
}
}
impl<SourceHead, SourceTail> Transfigurator<Nihil, Nihil> for Coniunctio<SourceHead, SourceTail> {
#[inline(always)]
fn transfigure(self) -> Nihil {
Nihil
}
}
impl<
SourceHead,
SourceTail,
TargetName,
TargetHead,
TargetTail,
TransfigHeadIndex,
TransfigTailIndices,
>
Transfigurator<
Coniunctio<TargetHead, TargetTail>,
Coniunctio<TransfigHeadIndex, TransfigTailIndices>,
> for Field<TargetName, Coniunctio<SourceHead, SourceTail>>
where
Coniunctio<SourceHead, SourceTail>: Transfigurator<
Coniunctio<TargetHead, TargetTail>,
Coniunctio<TransfigHeadIndex, TransfigTailIndices>,
>,
{
#[inline(always)]
fn transfigure(self) -> Coniunctio<TargetHead, TargetTail> {
self.value.transfigure()
}
}
impl<
SourceHead,
SourceTail,
TargetHeadName,
TargetHeadValue,
TargetTail,
PluckSourceHeadNameIndex,
TransfigSourceHeadValueIndices,
TransfigTailIndices,
>
Transfigurator<
Coniunctio<Field<TargetHeadName, TargetHeadValue>, TargetTail>,
Coniunctio<
DoTransfig<PluckSourceHeadNameIndex, TransfigSourceHeadValueIndices>,
TransfigTailIndices,
>,
> for Coniunctio<SourceHead, SourceTail>
where
Coniunctio<SourceHead, SourceTail>:
ByNameFieldPlucker<TargetHeadName, PluckSourceHeadNameIndex>,
Field<
TargetHeadName,
<Coniunctio<SourceHead, SourceTail> as ByNameFieldPlucker<
TargetHeadName,
PluckSourceHeadNameIndex,
>>::TargetValue,
>: Transfigurator<TargetHeadValue, TransfigSourceHeadValueIndices>,
<Coniunctio<SourceHead, SourceTail> as ByNameFieldPlucker<
TargetHeadName,
PluckSourceHeadNameIndex,
>>::Remainder: Transfigurator<TargetTail, TransfigTailIndices>,
{
#[inline(always)]
fn transfigure(self) -> Coniunctio<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 transfigrified_value: TargetHeadValue = source_field_for_head_target_name.transfigure();
let as_field: Field<TargetHeadName, TargetHeadValue> =
field_with_name(name, transfigrified_value);
Coniunctio {
head: as_field,
tail: remainder.transfigure(),
}
}
}
impl<Source, Target, TransfigIndices>
Transfigurator<Target, NominataUniversalisTransfigIndicesWrapper<TransfigIndices>> for Source
where
Source: NominataUniversalis,
Target: NominataUniversalis,
<Source as NominataUniversalis>::Repr:
Transfigurator<<Target as NominataUniversalis>::Repr, TransfigIndices>,
{
#[inline(always)]
fn transfigure(self) -> Target {
let source_as_repr = self.into();
let source_transfigged = source_as_repr.transfigure();
<Target as NominataUniversalis>::from(source_transfigged)
}
}
impl<Source, TargetName, TargetValue, TransfigIndices>
Transfigurator<TargetValue, PluckedNominataUniversalisIndicesWrapper<TransfigIndices>>
for Field<TargetName, Source>
where
Source: NominataUniversalis,
TargetValue: NominataUniversalis,
Source: Transfigurator<TargetValue, TransfigIndices>,
{
#[inline(always)]
fn transfigure(self) -> TargetValue {
self.value.transfigure()
}
}
#[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 = (La, Lb, Lc);
#[allow(non_camel_case_types)]
type name = (Ln, La, Lm, Le);
#[allow(non_camel_case_types)]
type age = (La, Lg, Le);
#[allow(non_camel_case_types)]
type is_admin = (Li, Ls, DoubleUnderscore, La, Ld, Lm, Li, Ln);
#[allow(non_camel_case_types)]
type inner = (Li, Ln, Ln, Le, Lr);
#[test]
fn test_label_new_building() {
let l1 = field!(abc, 3);
assert_eq!(l1.value, 3);
assert_eq!(l1.name, "abc");
let l2 = field!(abc, 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!(abc, 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!(age, 3);
assert_eq!(f1, f2);
}
#[test]
fn test_field_debug() {
let field = field!(age, 3);
let coniunctio_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!(age, 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!(age, 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!(age, 30),]);
}
#[test]
fn test_ref_pluck_by_name() {
let record = &hlist![
field!(is_admin, true),
field!(name, "Joe".to_string()),
field!(age, 30),
];
let (name, r): (Field<name, _>, _) = record.pluck_by_name();
assert_eq!(name.value, "Joe");
assert_eq!(r, hlist![&field!(is_admin, true), &field!(age, 30),]);
}
#[test]
fn test_unlabelling() {
let labelled_hlist = hlist![field!(name, "joe"), field!(age, 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!(age, 3)];
let value_labelled: HList![ValueField<&str>, ValueField<isize>] =
labelled_hlist.into_value_labelled();
let coniunctio_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_transfigure_hnil_identity() {
let hnil_again: Nihil = Nihil.transfigure();
assert_eq!(Nihil, hnil_again);
}
#[test]
fn test_transfigure_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 source: Source = hlist!(field!(name, "joe"), field!(age, 3), field!(is_admin, true));
let t_hcons: Target = source.transfigure();
assert_eq!(t_hcons, hlist!(field!(age, 3)));
}
#[test]
fn test_transfigure_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 source: Source = hlist!(field!(name, "joe"), field!(age, 3), field!(is_admin, true));
let t_hcons: Target = source.transfigure();
assert_eq!(t_hcons, hlist!(field!(is_admin, true), field!(name, "joe")));
}
#[test]
fn test_transfigure_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.transfigure();
assert_eq!(
target,
hlist![
field!(is_admin, true),
field!(name, hlist![field!(is_admin, true)]),
]
);
}
#[test]
fn test_transfigure_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 source: Source = hlist!(field!(name, "joe"), field!(age, 3), field!(is_admin, true));
let t_hcons: Target = source.transfigure();
assert_eq!(
t_hcons,
hlist!(field!(is_admin, true), field!(name, "joe"), field!(age, 3))
);
}
#[test]
fn test_transfigure_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.transfigure();
assert_eq!(
target,
hlist![field!(name, "joe"), field!(age, 32), field!(is_admin, true)]
);
}
#[test]
fn test_transfigure_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.transfigure();
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.transfigure();
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.transfigure();
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.transfigure();
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.transfigure();
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.transfigure();
assert_eq!(
target,
hlist![field!(name, "Joe"), field!(inner, target_inner)]
);
}
}