Crate rgraph [] [src]

The rGaph crate:

This library provides the mechanisms to define a directed acyclic graph of tasks. Once the graph is generated, a solver object can be instantiated to execute any of the tasks defined. In order to satisfy the input of such task, all the producer tasks will be executed as well.

A task can be defined like you would define a function, it requires: - A name - A list of inputs, that well may be empty. - A list of outputs, which can be empty as well. - Body, executing the code necessary to produce the outputs out of the inputs.

The macro create_node! will help you out with this task:

use rgraph::*;
 
create_node!(
         task_name  (a: u32, b : u32) -> (output: u32) {
             // return is done by assigning to the output variable
             output = a + b;
         }
     );

The body of the task will be executed by a move lambda, this enforces some guarantees. Nevertheless if the tasks need to execute some side effects, you may keep in mind that: - Objects need to be cloned into the task scope. - Only runtime borrowing can be checked at this point. - The Solver has no knowledge of data changes done via global access. It only tracks assets registered as inputs or outputs of the task. For this reason tasks may not be executed a second time as long as the inputs do not change. This may turn into side effects not happening because the requirements were not declared correctly.

Once the tasks are defined, you can bind the input assets to the output produced by other task or feed directly into the Solver.

use rgraph::*;
let mut g = Graph::new();
  
g.add_node(create_node!(
         task1  () -> (out_asset: u32) {
             // .... task body 
             out_asset = 1;
         }
     ));
      
g.add_node(create_node!(
         task2  (in_asset : u32) -> () {
             // .... task body 
         }
     ));
  
g.bind_asset("task1::out_asset", "task2::in_asset").expect(" tasks and assets must exist");

Finally, to execute the Graph: - Create an assets cache object (which can be reused to execute the graph again) - Create a solver, to be used one single time and then dropped.

use rgraph::*;
let mut g = Graph::new();
  
 // ... create graph and bind the assets
 
let mut cache = ValuesCache::new();
let mut solver = GraphSolver::new(&g, &mut cache);
// terminal tasks are those which do not produce output
// the following line will traverse the graph and execute all tasks needed
// to satisfy the terminal tasks.
solver.execute_terminals().unwrap();

Macros

asset_name
create_node

Macro to generate a Node (Task). It requires: a name (as used in the solver to execute it), a set of inputs, a set of outputs, and a set of statements which are the body of the task

Structs

Graph

The graph class itself. It holds the static information about the tasks (Nodes) and how they depend on each other by waiting on resources (Assets)

GraphSolver

The graph solver is a transient object which can execute the tasks described in a graph. It is designed to be generated and dropped on every execution.

Node

Generic that stores the information required to execute arbitrary tasks Please use create_node macro to instantiate this objects

Enums

GraphError

Errors that may happen during Graph construction

SolverError

Errors that may happen during a Solver instance execution

SolverStatus

Type to differentiate cached tasks from executed ones

Traits

Cache
Comparable

this trait allows us to overload behavior for custom types in this manner comparison can be optimized or bypassed for custom types

NodeRunner

helper trait that hides heterogeneous tasks behind a common interface

Type Definitions

ValuesCache

type used to store results of executions and pass it to further solver instances