1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use typenum::{IsEqual, B0, B1};

use crate::cons::{Cons, Nil};
use crate::label::Label;

/// Marker struct signifying `true`.
pub struct True;
/// Marker struct signifying `false`.
pub struct False;

/// Trait for types that signify `true` or `false`.
pub trait Bool {
    /// `true` or `false` value
    const VALUE: bool;
}
impl Bool for True {
    const VALUE: bool = true;
}
impl Bool for False {
    const VALUE: bool = false;
}

/// Conversion trait for types that have a logical `true` or `false` meaning
pub trait ToBool {
    /// Conversion output (typically either [True](struct.True.html) or [False](struct.False.html))
    type Output: Bool;
}
impl ToBool for B1 {
    type Output = True;
}
impl ToBool for B0 {
    type Output = False;
}

/// Label equality.
///
/// Checks whether two types that implement [Label](trait.Label.html) are the same.
pub trait LabelEq<L> {
    /// [True](struct.True.html) if labels are equal, [False](struct.False.html) otherwise.
    type Output: Bool;
}

impl<L, M> LabelEq<M> for L
where
    L: Label,
    M: Label,
    L::Uid: IsEqual<M::Uid>,
    <L::Uid as IsEqual<M::Uid>>::Output: ToBool,
{
    type Output = <<L::Uid as IsEqual<M::Uid>>::Output as ToBool>::Output;
}

/// Check to see if a target label is a list member.
pub trait Member<TargetL> {
    /// [True](struct.True.html) if `TargetL` is a member, [False](struct.False.html) otherwise.
    type Output: Bool;
}

impl<TargetL> Member<TargetL> for Nil {
    type Output = False;
}
impl<TargetL, L, T> Member<TargetL> for Cons<L, T>
where
    L: Label + LabelEq<TargetL>,
    Self: MemberMatch<TargetL, <L as LabelEq<TargetL>>::Output>,
{
    type Output = <Self as MemberMatch<TargetL, <L as LabelEq<TargetL>>::Output>>::Output;
}

/// Helper trait for [Member](trait.Member.html).
pub trait MemberMatch<L, HeadMatch> {
    /// [True](struct.True.html) if `TargetL` is a member, [False](struct.False.html) otherwise.
    type Output: Bool;
}

impl<TargetL, L, T> MemberMatch<TargetL, True> for Cons<L, T> {
    type Output = True;
}
impl<TargetL, L, T> MemberMatch<TargetL, False> for Cons<L, T>
where
    L: Label,
    T: Member<TargetL>,
{
    type Output = <T as Member<TargetL>>::Output;
}

#[cfg(test)]
mod tests {
    use crate::*;

    #[label(name = "My Label", crate=crate)]
    struct Label1;

    #[label(type=u8, crate=crate)]
    struct Label2;

    #[label(crate=crate)]
    struct Label3;

    #[test]
    fn label_eq() {
        assert!(<Label1 as LabelEq<Label1>>::Output::VALUE);
        assert!(<Label2 as LabelEq<Label2>>::Output::VALUE);
        assert!(<Label3 as LabelEq<Label3>>::Output::VALUE);

        assert!(!<Label1 as LabelEq<Label2>>::Output::VALUE);
        assert!(!<Label1 as LabelEq<Label3>>::Output::VALUE);

        assert!(!<Label2 as LabelEq<Label1>>::Output::VALUE);
        assert!(!<Label2 as LabelEq<Label3>>::Output::VALUE);

        assert!(!<Label3 as LabelEq<Label1>>::Output::VALUE);
        assert!(!<Label3 as LabelEq<Label2>>::Output::VALUE);
    }

    #[test]
    fn member() {
        // type-based member testing
        type TestList = LCons<Label1, LCons<Label2, Nil>>;
        assert!(<TestList as Member<Label1>>::Output::VALUE);
        assert!(<TestList as Member<Label2>>::Output::VALUE);
        assert!(!<TestList as Member<Label3>>::Output::VALUE);

        // value-based member testing
        let list = labels![Label1, Label2];
        assert!(list.has_label(Label1));
        assert!(list.has_label(Label2));
        assert!(!list.has_label(Label3));
    }
}