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
//! # Library to perform operations over dependency graphs. //! //! This library allow running iterative operations over a dependency graph in //! the correct evaluation order, or will fail if there are a circular //! dependencies. //! //! To use this library, you create a list of [Nodes](trait.Node.html) //! containing dependency information (which node depends on which). You then //! create a [Resolver](struct.Resolver.html) which will allow you to traverse //! the graph so that you will always get an item for which all dependencies //! have been processed. //! //! ## Processing order //! //! Resolvers have two methods: one for sequential operations and one for //! parallel (multi-threaded) operations. In the first case, it's easy to know //! in which order nodes can be processed, as only one node will be processed //! at a time. However, in parallel operations, we need to know if a given node //! is done processing. //! //! This leads to a situation where a given worker thread might not be able to //! pull a node temporarily, as it needs to wait for another worker to finish //! processing another node. //! //! Let's look at the following case: //! //! ```text,no_run //! (A) <-| //! |-- [E] <-|-- [G] //! (B) <-| | //! |-- [F] <-|-- [H] //! [C] <-| //! ``` //! //! In this case, the nodes __E__ and __F__ are dependent on __A__, __B__ and //! __C__ and __G__ and __H__ are dependent on both __E__ and __F__. If we //! process the nodes with two workers, they might pick up nodes A and B first. //! Since these nodes don't have any dependencies, there is no problem right //! now. //! //! ```text,no_run //! [ ] <-| //! |-- [E] <-|-- [G] //! [ ] <-| | //! |-- [F] <-|-- [H] //! (C) <-| //! ``` //! //! When one of the worker is done, it can immediately start working on node //! __C__, as it does not have any dependencies. However, when the second //! worker is done, there are no available nodes for processing: we need to //! wait until __C__ is processed before we can start working on __E__ or //! __F__. One of the worker will then stay idle until the other one is done. //! //! ```text,no_run //! [ ] <-| //! |-- (E) <-|-- [G] //! [ ] <-| | //! |-- (F) <-|-- [H] //! [ ] <-| //! ``` //! //! Once that is done, both workers can work on __E__ and __F__. However, if //! __E__ takes only a fraction of the time compared to __F__, we will end up //! in the same situation, as there are no nodes without un-processed //! dependencies. //! //! ## Basic usage //! //! ```rust //! use depgraph::{Node, Resolver,StrNode}; //! //! fn my_graph() { //! // Create a list of nodes //! let mut root = StrNode::new("root"); //! let mut dep1 = StrNode::new("dep1"); //! let mut dep2 = StrNode::new("dep2"); //! let leaf = StrNode::new("leaf"); //! //! // Map their connections //! root.add_dep(dep1.id()); //! root.add_dep(dep2.id()); //! dep1.add_dep(leaf.id()); //! dep2.add_dep(leaf.id()); //! //! // Create a resolver //! let nodes = vec![root, dep1, dep2, leaf]; //! let resolver = Resolver::new(&nodes); //! //! // Run an operation over all nodes in the graph. //! // The function receives the identity value from the node, not the //! // entire node (e.g. "root", "dep1", etc. in this case). //! resolver //! .par_for_each(&|node| { //! println!("{}", node) //! }) //! .unwrap(); //! } //! ``` //! //! ## Create your own node type //! //! This library provides a node for string types //! [`StrNode`](struct.StrNode.html). //! //! However, you may want to implement your own node type to hold another type //! of identity information. For this purpose, you can implement the //! [`Node trait`](trait.Node.html). mod dep; pub mod error; mod resolver; pub use dep::{Node, StrNode}; pub use resolver::Resolver;