graphity 2.0.0

Model signal flow between nodes within a directed graph.
Documentation
//! 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};