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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
//! Graphity offers tools to model signal flow within directed graphs.
//!
//! There are two main parts playing a role here. First are nodes implemented by
//! the user. These nodes can have arbitrary number of inputs and outputs, and
//! an optional method performing operations over provided data. The second part
//! is then a signal graph structure generated by this library. It allows the
//! user to register previosly defined nodes, wire them up and let signal flow
//! between them the user to register previosly defined nodes, wire them up and
//! let signal flow between them.
//!
//! All the core concepts are illustrated by the following example. Read through
//! the documentation of individual structs and traits to learn more.
//!
//! # Example
//!
//! This example will work with a signal graph containing 3 types of nodes:
//!
//! * `Generator` –⁠ pushing given value throught its output.
//! * `Sum` –⁠ accepting two inputs and returning the sum of them.
//! * `Echo` –⁠ accepting one input and printing it on the standard output as a
//!   side-effect.
//!
//! They will be wired up as following:
//!
//! ```text
//!      [Echo]
//!  Λ      \     __
//!  |       \   /  |  |
//!  |       [Sum]  |  |
//!  |       /   \__|  V
//!         /
//!    [Generator(1)]
//! ```
//!
//! Generator sends value `1` to the first input of `Sum`. `Sum` sends the
//! calculated result to its own second input, forming a loop. It will also send
//! a copy of the result to `Echo`, so we can observe the current value.
//!
//! ## Defining nodes
//!
//! First, let's define all the modules. We'll start with `Sum` since it covers
//! most of the important parts:
//!
//! ```
//! # use graphity::Node;
//! #[derive(Default)]
//! pub struct Sum {
//!     input1: i32,
//!     input2: i32,
//!     output: i32,
//! }
//!
//! #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
//! pub enum SumConsumer {
//!     In1,
//!     In2,
//! }
//!
//! #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
//! pub struct SumProducer;
//!
//! impl Node<i32> for Sum {
//!     type Consumer = SumConsumer;
//!     type Producer = SumProducer;
//!
//!     fn write(&mut self, consumer: Self::Consumer, input: i32) {
//!         match consumer {
//!             Self::Consumer::In1 => self.input1 = input,
//!             Self::Consumer::In2 => self.input2 = input,
//!         }
//!     }
//!
//!     fn read(&self, _producer: Self::Producer) -> i32 {
//!         self.output
//!     }
//!
//!     fn tick(&mut self) {
//!         self.output = self.input1 + self.input2;
//!     }
//! }
//! ```
//!
//! There is lot to discuss here, let's split it into parts.
//!
//! The first part simply defines the structure of the node. In this case, it
//! has one field per each input/output to work with:
//!
//! ```
//! #[derive(Default)]
//! pub struct Sum {
//!     input1: i32,
//!     input2: i32,
//!     output: i32,
//! }
//! ```
//!
//! Next up, there is the definition of node's consumers. A consumer is simply
//! an input pin of the node to which we can attach an edge. In this case it is
//! an enum that can be one of two invariants, one for each input:
//!
//! ```
//! #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
//! pub enum SumConsumer {
//!     In1,
//!     In2,
//! }
//! ```
//!
//! Similarly, node's producer is an output pin. In the case of this node, there
//! is only one possible output, so we define a primitive struct as the type:
//!
//! ```
//! #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
//! pub struct SumProducer;
//! ```
//!
//! Each node provided by the user must implement the [Node
//! trait](trait.Node.html). You can see that this is where we link previously
//! defined consumers and producers. Note that the node is implemented for type
//! `i32`. That is the payload type that will be used for data flowing between
//! nodes. All consumers and producers within a graph must work with this type:
//!
//! ```ignore
//! impl Node<i32> for Sum {
//!     type Consumer = SumConsumer;
//!     type Producer = SumProducer;
//!     // ...
//! }
//! ```
//!
//! The `write` method will be used to provide data to the node. It passes the
//! assigned `Consumer` to specify to which consumer is the data meant for. In
//! this case, our `Sum` node has two inputs, one for each number to be summed:
//!
//! ```ignore
//!     fn write(&mut self, consumer: Self::Consumer, input: i32) {
//!         match consumer {
//!             Self::Consumer::In1 => self.input1 = input,
//!             Self::Consumer::In2 => self.input2 = input,
//!         }
//!     }
//! ```
//!
//! Its counterpart is the `read` method which will be called from the outside
//! to read data produced by the node. Since our node has only one producer
//! available, we can safely ignore the given `producer` value:
//!
//! ```ignore
//!     fn read(&self, _producer: Self::Producer) -> i32 {
//!         self.output
//!     }
//! ```
//!
//! Finally, we need to define the `tick` method which will process all the data
//! set on the input and save the result on the output:
//!
//! ```ignore
//!     fn tick(&mut self) {
//!         self.output = self.input1 + self.input2;
//!     }
//! ```
//!
//! The remaining two nodes for completeness:
//!
//! ```
//! # use graphity::Node;
//! pub struct Generator(i32);
//!
//! #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
//! pub enum GeneratorConsumer {}
//!
//! #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
//! pub struct GeneratorProducer;
//!
//! impl Node<i32> for Generator {
//!     type Consumer = GeneratorConsumer;
//!     type Producer = GeneratorProducer;
//!
//!     fn read(&self, _producer: Self::Producer) -> i32 {
//!         self.0
//!     }
//! }
//!
//! #[derive(Default)]
//! pub struct Echo {
//!     input: i32,
//! }
//!
//! #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
//! pub struct EchoConsumer;
//!
//! #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
//! pub enum EchoProducer {}
//!
//! impl Node<i32> for Echo {
//!     type Consumer = EchoConsumer;
//!     type Producer = EchoProducer;
//!
//!     fn write(&mut self, _consumer: Self::Consumer, input: i32) {
//!         self.input = input;
//!     }
//!
//!     fn tick(&mut self) {
//!         println!("Echo: {}", self.input);
//!     }
//! }
//! ```
//!
//! ## Defining the graph
//!
//! The nodes do not bring much value on their own. The goal of this library is
//! to make it easy to model signal flow between these nodes within a graph.
//!
//! To build such a graph, all you need to do is to call `graphity`, defining
//! the name of the generated structure, the type of the payload passed between
//! nodes and list of the previously defined nodes:
//!
//! ```
//! # use graphity_nodes::*;
//! # #[macro_use]
//! # extern crate graphity;
//! # fn main() {
//! graphity!(
//!     Graph<i32>;
//!     Generator = {Generator, GeneratorConsumer, GeneratorProducer},
//!     Echo = {Echo, EchoConsumer, EchoProducer},
//! );
//! # }
//! ```
//!
//! Note that the macro is called within its own module to prevent conflicts
//! with the rest of you code.
//!
//! ## Wiring it all up
//!
//! Finally, let's instantiate such a graph, add nodes, connect them a and let
//! the signal flow.
//!
//! First, let's create an instance of the previously generated graph:
//!
//! ```ignore
//! let mut graph = Graph::new();
//! ```
//!
//! Then add nodes to it. The returned value is actually an index of the stored
//! node and can be later used to access consumers and producers or remove the
//! node:
//!
//! ```ignore
//! let generator = graph.add_node(Generator(1));
//! let sum = graph.add_node(Sum::default());
//! let echo = graph.add_node(Echo::default());
//! ```
//!
//! As the next step, we can create edges between producers and consumers of
//! available nodes to form the topology described above:
//!
//! ```ignore
//! graph.add_edge(
//!     generator.producer(GeneratorProducer),
//!     sum.consumer(SumConsumer::In1),
//! );
//! graph.add_edge(
//!     sum.producer(SumProducer),
//!     sum.consumer(SumConsumer::In2),
//! );
//! graph.add_edge(
//!     sum.producer(SumProducer),
//!     echo.consumer(EchoConsumer),
//! );
//! ```
//!
//! Once all is wired up, we can trigger tick of the graph. When this happens,
//! the graph is traversed, all individual nodes ticket and their output passed
//! on to the input of their connected nodes. In this example, you can see that
//! the `Echo` node keeps reporting increasing number due to the cycle feeding
//! back the output of the `Sum` to its input.
//!
//! ```ignore
//! graph.tick();
//! graph.tick();
//! graph.tick();
//! ```
//!
//! ```text
//! Echo: 1
//! Echo: 2
//! Echo: 3
//! ```
//!
//! You can find the full executable version of this example under the [project
//! sources](https://github.com/zlosynth/graphity/tree/main/examples).
//!
//! Learn more about individual types in this documentation. Reading about the
//! generated [`SignalGraph`](signal/struct.SignalGraph.html) would be a good
//! start.

#![no_std]

extern crate alloc;

pub mod error;
mod feedback;
mod graph;
mod graphity;
mod internal;
pub mod node;
pub mod signal;
mod sort;

pub use error::Error;
pub use node::{Node, NodeIndex, NodeWrapper};