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
131
132
133
134
135
136
137
138
139
140
141
142
143
/*
The current structure of the composite system is
similar to a linked list but which each node containing a type instead
of an object. So effectively, we build a list of types at compile
time, terminating with the unit type, and use that list to build a nested
data structure where each level of nesting is responsible for one of the
types in that list (the most deeply nested level only containing the unit
type).

To understand how this is handy,lets go through the usage path, from here
to the application writer.

1. The zero_v library: Uses this to define generic collections of objects.
2. The intermediate library: Uses the nesting level and recursion to define an
   iterator/ some other output over any collection of objects implementing
   their trait which executes one or more functions on each object at each
   level and combines the outputs in some way.
3. The application author: Defines the specific collection of objects at
   compile time and uses the behavior defined by the intermediate library
   to get some result or to plug that collection into some type exposed
   by the intermediate library.
*/

/// A type representing a collection of zero or more objects.
#[derive(Debug, PartialEq)]
pub struct Composite<A: NextNode> {
    /// Can be of any type implementing the NextNode trait. Typically this will
    /// be a node whose `next` field implements NextNode (representing a
    /// collection of one or more nested nodes) or the unit type
    /// (representing an empty composite).
    pub head: A,
}

impl<A: NextNode> Composite<A> {
    /// Generates a new Composite
    ///
    /// # Arguments
    ///
    /// * `head` - The first node in the data structure or the unit type.
    pub fn new(head: A) -> Self {
        Self { head }
    }
}

/// Represents a collection of one or more objects.
#[derive(Debug, PartialEq)]
pub struct Node<A, B: NextNode> {
    /// The object held in this node
    pub data: A,
    /// Next is any type implementing the NextNode trait. Typically this will
    /// be a node whose next field also implements NextNode (representing a
    /// collection of one or more nested node) or the unit type
    /// (representing an empty composite).
    pub next: B,
}

impl<A, B: NextNode> Node<A, B> {
    /// Build a new node
    ///
    /// # Arguments
    ///
    /// * `data` - The object held in this node in the composite
    /// * `next` - The next node in the data structure.
    pub fn new(data: A, next: B) -> Self {
        Self { data, next }
    }
}

impl<A> Node<A, ()> {
    /// Build a new Node where the next field is the unit type.
    pub fn base(data: A) -> Self {
        Self { data, next: () }
    }
}

/// A Marker trait for types which can be nested in a node's next field
/// or Composite's head field. Implemented for the unit type
/// or a Node whose next field implements NextNode.
// Basically what we're doing here is building something similar to an option
// using traits. Obviously, an enum does not suit this situation since an enum
// is used in cases where we don't know whether the object will be of variant
// X or Y at compile time. In this case, we don't know this information while
// writing this library, but the library user will know the exact type of
// NextNode at compile time.
pub trait NextNode {}
impl NextNode for () {}
impl<A, B: NextNode> NextNode for Node<A, B> {}

/// Takes a list of objects and uses them to build a nested node object
/// with one of the original objects contained in the data field of each node.
///
/// # Example usage
/// ```
/// use zero_v::{compose_nodes, Node};
///
/// let nodes = compose_nodes!(1, 2);
/// assert_eq!(nodes, Node::new(1, Node::new(2, ())));
/// ```
#[macro_export]
macro_rules! compose_nodes {
    () => {
        ()
    };
    ($val: expr) => {
       $crate::Node::base($val)
    };
    ($left: expr, $($right: expr), +) => {
        $crate::Node::new($left, $crate::compose_nodes!( $($right), +))
    };
}

/// Takes a list of objects and uses them to build a composite
/// with one of the original objects contained in the data field of each node
/// (or a single unit type if the list is empty).
///
/// # Example usage
/// ```
/// use zero_v::{compose, Composite, Node};
///
/// let nodes = compose!(1, 2);
/// assert_eq!(nodes, Composite::new(Node::new(1, Node::base(2))));
/// ```
#[macro_export]
macro_rules! compose {
    ($($right: expr), *) => {
        $crate::Composite::new($crate::compose_nodes!( $($right), *))
    };
}

#[cfg(test)]
mod test {
    use super::{Composite, Node};
    #[test]
    fn can_build_composites_with_compose_macro() {
        assert_eq!(compose!(), Composite::new(()));
        assert_eq!(compose!(0), Composite::new(Node::base(0)));
        assert_eq!(compose!(0, 1), Composite::new(Node::new(0, Node::base(1))));
        assert_eq!(
            compose!(0, 1, 2),
            Composite::new(Node::new(0, Node::new(1, Node::base(2))))
        );
    }
}