use crate::hlist::{Coniunctio, HList, Nihil};
use crate::indices::{Here, There};
use crate::labelled::{Field, field_with_name};
pub trait HabetCampum<Label, Value, Index> {
fn get_field(&self) -> &Value;
fn get_field_mut(&mut self) -> &mut Value;
}
impl<Label, Value, Tail: HList> HabetCampum<Label, Value, Here>
for Coniunctio<Field<Label, Value>, Tail>
{
#[inline]
fn get_field(&self) -> &Value {
&self.head.value
}
#[inline]
fn get_field_mut(&mut self) -> &mut Value {
&mut self.head.value
}
}
impl<Label, Value, Head, Tail, TailIndex> HabetCampum<Label, Value, There<TailIndex>>
for Coniunctio<Head, Tail>
where
Tail: HabetCampum<Label, Value, TailIndex>,
{
#[inline]
fn get_field(&self) -> &Value {
self.tail.get_field()
}
#[inline]
fn get_field_mut(&mut self) -> &mut Value {
self.tail.get_field_mut()
}
}
pub trait Extendo<Label, Value>: Sized {
type Output;
fn extend(self, name: &'static str, value: Value) -> Self::Output;
}
impl<Label, Value> Extendo<Label, Value> for Nihil {
type Output = Coniunctio<Field<Label, Value>, Nihil>;
#[inline]
fn extend(self, name: &'static str, value: Value) -> Self::Output {
Coniunctio {
head: field_with_name(name, value),
tail: Nihil,
}
}
}
impl<Label, Value, Head, Tail: HList> Extendo<Label, Value> for Coniunctio<Head, Tail> {
type Output = Coniunctio<Field<Label, Value>, Coniunctio<Head, Tail>>;
#[inline]
fn extend(self, name: &'static str, value: Value) -> Self::Output {
Coniunctio {
head: field_with_name(name, value),
tail: self,
}
}
}
pub trait Restricto<Label, Index>: Sized {
type Value;
type Remainder;
fn restrict(self) -> (Self::Value, Self::Remainder);
}
impl<Label, Value, Tail: HList> Restricto<Label, Here> for Coniunctio<Field<Label, Value>, Tail> {
type Value = Value;
type Remainder = Tail;
#[inline]
fn restrict(self) -> (Self::Value, Self::Remainder) {
(self.head.value, self.tail)
}
}
impl<Label, Head, Tail, TailIndex> Restricto<Label, There<TailIndex>> for Coniunctio<Head, Tail>
where
Tail: Restricto<Label, TailIndex>,
{
type Value = Tail::Value;
type Remainder = Coniunctio<Head, Tail::Remainder>;
#[inline]
fn restrict(self) -> (Self::Value, Self::Remainder) {
let (value, tail_remainder) = self.tail.restrict();
(
value,
Coniunctio {
head: self.head,
tail: tail_remainder,
},
)
}
}
pub trait Confluo<Other>: Sized {
type Output;
fn merge(self, other: Other) -> Self::Output;
}
impl<T: HList> Confluo<Nihil> for T {
type Output = T;
#[inline]
fn merge(self, _: Nihil) -> Self::Output {
self
}
}
impl<Head, Tail: HList> Confluo<Coniunctio<Head, Tail>> for Nihil {
type Output = Coniunctio<Head, Tail>;
#[inline]
fn merge(self, other: Coniunctio<Head, Tail>) -> Self::Output {
other
}
}
impl<H1, T1: HList + Confluo<Coniunctio<H2, T2>>, H2, T2: HList> Confluo<Coniunctio<H2, T2>>
for Coniunctio<H1, T1>
{
type Output = Coniunctio<H1, T1::Output>;
#[inline]
fn merge(self, other: Coniunctio<H2, T2>) -> Self::Output {
Coniunctio {
head: self.head,
tail: self.tail.merge(other),
}
}
}
pub trait Renomino<OldLabel, NewLabel, Index>: Sized {
type Output;
fn rename(self, new_name: &'static str) -> Self::Output;
}
impl<OldLabel, NewLabel, Value, Tail: HList> Renomino<OldLabel, NewLabel, Here>
for Coniunctio<Field<OldLabel, Value>, Tail>
{
type Output = Coniunctio<Field<NewLabel, Value>, Tail>;
#[inline]
fn rename(self, new_name: &'static str) -> Self::Output {
Coniunctio {
head: field_with_name(new_name, self.head.value),
tail: self.tail,
}
}
}
impl<OldLabel, NewLabel, Head, Tail, TailIndex> Renomino<OldLabel, NewLabel, There<TailIndex>>
for Coniunctio<Head, Tail>
where
Tail: Renomino<OldLabel, NewLabel, TailIndex>,
{
type Output = Coniunctio<Head, Tail::Output>;
#[inline]
fn rename(self, new_name: &'static str) -> Self::Output {
Coniunctio {
head: self.head,
tail: self.tail.rename(new_name),
}
}
}
pub trait Muto<Label, NewValue, Index>: Sized {
type OldValue;
type Output;
fn modify<F>(self, f: F) -> Self::Output
where
F: FnOnce(Self::OldValue) -> NewValue;
}
impl<Label, OldValue, NewValue, Tail: HList> Muto<Label, NewValue, Here>
for Coniunctio<Field<Label, OldValue>, Tail>
{
type OldValue = OldValue;
type Output = Coniunctio<Field<Label, NewValue>, Tail>;
#[inline]
fn modify<F>(self, f: F) -> Self::Output
where
F: FnOnce(Self::OldValue) -> NewValue,
{
Coniunctio {
head: field_with_name(self.head.name, f(self.head.value)),
tail: self.tail,
}
}
}
impl<Label, NewValue, Head, Tail, TailIndex> Muto<Label, NewValue, There<TailIndex>>
for Coniunctio<Head, Tail>
where
Tail: Muto<Label, NewValue, TailIndex>,
{
type OldValue = Tail::OldValue;
type Output = Coniunctio<Head, Tail::Output>;
#[inline]
fn modify<F>(self, f: F) -> Self::Output
where
F: FnOnce(Self::OldValue) -> NewValue,
{
Coniunctio {
head: self.head,
tail: self.tail.modify(f),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::labelled::chars::*;
type Name = (Ln, La, Lm, Le);
type Age = (La, Lg, Le);
#[test]
fn test_habet_campum_head() {
let record = hlist![field!(Name, "Alice")];
let name: &&str = HabetCampum::<Name, &str, Here>::get_field(&record);
assert_eq!(*name, "Alice");
}
#[test]
fn test_habet_campum_tail() {
let record = hlist![field!(Age, 30i32), field!(Name, "Alice")];
let name: &&str = HabetCampum::<Name, &str, There<Here>>::get_field(&record);
assert_eq!(*name, "Alice");
}
#[test]
fn test_extendo_nihil() {
let record: Coniunctio<Field<Name, &str>, Nihil> =
Extendo::<Name, &str>::extend(Nihil, "name", "Alice");
assert_eq!(record.head.value, "Alice");
}
#[test]
fn test_extendo_non_empty() {
let record = hlist![field!(Name, "Alice")];
let extended = Extendo::<Age, i32>::extend(record, "age", 30i32);
assert_eq!(extended.head.value, 30);
assert_eq!(extended.tail.head.value, "Alice");
}
#[test]
fn test_restricto_head() {
let record = hlist![field!(Name, "Alice"), field!(Age, 30i32)];
let (name, rest): (&str, _) = Restricto::<Name, Here>::restrict(record);
assert_eq!(name, "Alice");
assert_eq!(rest.head.value, 30);
}
#[test]
fn test_restricto_tail() {
let record = hlist![field!(Age, 30i32), field!(Name, "Alice")];
let (name, rest): (&str, _) = Restricto::<Name, There<Here>>::restrict(record);
assert_eq!(name, "Alice");
assert_eq!(rest.head.value, 30);
}
#[test]
fn test_confluo_empty() {
let record = hlist![field!(Name, "Alice")];
let merged = record.merge(Nihil);
assert_eq!(merged.head.value, "Alice");
}
#[test]
fn test_confluo_two_records() {
let r1 = hlist![field!(Name, "Alice")];
let r2 = hlist![field!(Age, 30i32)];
let merged = r1.merge(r2);
assert_eq!(merged.head.value, "Alice");
assert_eq!(merged.tail.head.value, 30);
}
#[test]
fn test_muto() {
let record = hlist![field!(Age, 30i32)];
let modified = Muto::<Age, i32, Here>::modify(record, |age| age + 1);
assert_eq!(modified.head.value, 31);
}
#[test]
fn test_renomino() {
type NewName = (Ln, Le, Lw, DoubleUnderscore, Ln, La, Lm, Le);
let record = hlist![field!(Name, "Alice")];
let renamed: Coniunctio<Field<NewName, &str>, Nihil> =
Renomino::<Name, NewName, Here>::rename(record, "new_name");
assert_eq!(renamed.head.name, "new_name");
assert_eq!(renamed.head.value, "Alice");
}
}