use async_nodes::{BspEngine, BspGraph, BspOutputMapExt, EngineResult, node};
#[node("Source Text")]
async fn source_text() -> String {
"the quick brown fox jumps over the lazy dog".to_string()
}
#[node("Word Count")]
async fn word_count(text: String) -> i64 {
text.split_whitespace().count() as i64
}
#[node("Char Count")]
async fn char_count(text: String) -> i64 {
text.chars().count() as i64
}
#[node("Report")]
async fn report(words: i64, chars: i64) -> String {
format!("{words} words, {chars} characters")
}
#[tokio::main]
async fn main() {
let mut graph = BspGraph::new();
let source = graph.add_node(SourceTextNode::new());
let words = graph.add_node(WordCountNode::new());
let chars = graph.add_node(CharCountNode::new());
let report = graph.add_node(ReportNode::new());
graph.add_link(source.output(), words.text());
graph.add_link(source.output(), chars.text());
graph.add_link(words.output(), report.words());
graph.add_link(chars.output(), report.chars());
let engine = BspEngine::new(graph);
match engine.run(()).await {
Ok(EngineResult::Complete(state)) => {
let text: String = state.get_typed(report.output()).unwrap();
println!("✅ {text}");
println!("completed in {} supersteps", state.step);
}
Ok(EngineResult::Suspended(_)) => println!("graph asked for external input"),
Ok(EngineResult::StepLimitReached(_)) => println!("hit the step budget"),
Err(e) => eprintln!("graph failed: {e}"),
}
}