willdo 0.0.1

Task manager with DAG
Documentation
# execute jobs

Once you build a job graph, it can be executed by a `Runner`.

```rust
# (|| {
use crate::willdo::execution::Runner as _;
let graph = willdo::graph::Graph::builder().build()?;
let runner = willdo::execution::runner::simple::Simple;
let goals = ["bake".into()];
let events = runner.run(graph, &goals);
# Ok::<(),willdo::graph::BuildError>(()) })().expect("build");
```

This will produce a `Stream` of (`JobId`,`Progress`) events, consuming the graph.

It can also mutably borrow the graph so you can access it when done:

```rust
# (|| {
# use crate::willdo::execution::Runner as _;
let mut graph = willdo::graph::Graph::builder().build()?;
let runner = willdo::execution::Simple;
let goals = ["bake".into()];
let events = runner.run(&mut graph, &goals);
drop(events); // release the reference
println!("{graph}");
# Ok::<(),willdo::graph::BuildError>(()) })().expect("build");
```

Pick the jobs to run with `goals`. Passing an empty slice will run all top level jobs found.
Note that the `"bake"` goal does not exist in the graph and this is not an error to ask for missing jobs.
You can check that a desired job exists by examining the graph first.

Then you can process the events:


```rust
# use futures_lite::stream::StreamExt as _;
# async move {
# use crate::willdo::execution::Runner as _;
# let mut graph = willdo::graph::Graph::builder().build().expect("build");
# let runner = willdo::execution::Simple;
# let goals = ["bake".into()];
# let mut events = runner.run(&mut graph, &goals);
while let Some((id, progress)) = events.next().await {
    println!("{id}: {progress:?}");
}
#  };
```