Layout and Flows

An application is essentially a 'graph' of nodes. Where nodes can be seen as a 'function' that takes an input and produces an output or a standalone process that will just produce or consume data. The nodes are connected together by 'flows' that describe how the data is passed from one node to another.

Layout

It's really important when making a dataflow application to prepare the layout of the application before even writing the code. You can draw your application on paper and then translate it to code.

The first step is to create a DataflowLayout that describes the number of nodes, their names and their inputs/outputs. This is done by using the node method of the DataflowLayout struct. The node method takes a name and a closure.

#![allow(unused)]
fn main() {
use iridis_layout::prelude::{thirdparty::*, *};

let mut layout = DataflowLayout::new();

let (_source, output) = layout
    .node("source", async |builder: &mut Builder| {
        builder.output("out")
    })
    .await;
}

The node method will create a new node in the layout with the given name and will always return at least the associated NodeID object (which is just a label together with an uuid to represent this node). Inside the closure, you have access to a Builder that can add inputs, outputs and queries to the node. You can return anything you want from the closure, but it is recommended to return the result of the builder methods. This will allow you to use the PrimitiveID object later on to create the flows.

Flows

Once you have create the layout, it's recommended to build it as an immutable shared object:

#![allow(unused)]
fn main() {
let layout = layout.build();
}

Then you can create the connections (the flows) for this layout. This is done by using the Flows struct. The Flows struct takes a DataflowLayout and a closure that will be called to create the flows.

#![allow(unused)]
fn main() {
use iridis_flows::prelude::{thirdparty::*, *};

let flows = Flows::new(layout.clone(), async move |flows: &mut Connector| {
    flows.connect(op_in, output, None)?;
    flows.connect(input, op_out, None)?;

    Ok(())
})
.await?;
}

Note that the connect method will recognize each IO kind, so you don't have to worry about the order between the input and output, the query and the queryable. The connect method will also take care of the creation of the communication channels, and so you can adjust the capacity of the channel by passing a Some(capacity) value as the last parameter. If you pass None, the default capacity will be used (128).