use gantz_core::{
Edge, ROOT_STATE,
compile::push_eval_fn_name,
node::{self, Node, NodeState, WithPushEval, WithStateType},
};
use std::fmt::Debug;
use steel::{
SteelVal,
steel_vm::{engine::Engine, register_fn::RegisterFn},
};
use steel_derive::Steel;
fn node_push() -> node::Push<node::Expr> {
node::expr("'()").unwrap().with_push_eval()
}
fn node_counter() -> node::State<node::Expr, Counter> {
let expr = r#"
(begin
$push
(let ((value (counter-value state)))
(counter-increment state)
state))
"#;
node::expr(expr).unwrap().with_state_type::<Counter>()
}
#[derive(Clone, Debug, Default, PartialEq, Steel)]
struct Counter(u32);
impl Counter {
fn increment(&mut self) {
self.0 += 1;
}
fn value(&self) -> u32 {
self.0
}
}
impl NodeState for Counter {
const NAME: &str = "Counter";
fn register_fns(vm: &mut Engine) {
vm.register_fn("counter-increment", Self::increment);
vm.register_fn("counter-value", Self::value);
}
}
trait DebugNode: Debug + Node {}
impl<T> DebugNode for T where T: Debug + Node {}
#[test]
fn test_graph_with_counter() {
let mut g = petgraph::graph::DiGraph::new();
let push = node_push();
let counter = node_counter();
let push = g.add_node(Box::new(push) as Box<dyn DebugNode>);
let counter = g.add_node(Box::new(counter) as Box<_>);
g.add_edge(push, counter, Edge::from((0, 0)));
let module = gantz_core::compile::module(&g);
let mut vm = Engine::new_base();
vm.register_value(ROOT_STATE, SteelVal::empty_hashmap());
gantz_core::graph::register(&g, &[], &mut vm);
for f in module {
vm.run(format!("{f}")).unwrap();
}
for _ in 0..3 {
vm.call_function_by_name_with_args(&push_eval_fn_name(&[push.index()]), vec![])
.unwrap();
}
let res = node::state::extract::<Counter>(&vm, &[counter.index()])
.unwrap()
.unwrap();
assert_eq!(res, Counter(3));
node::state::update(&mut vm, &[counter.index()], Counter(0)).unwrap();
let res = node::state::extract::<Counter>(&vm, &[counter.index()])
.unwrap()
.unwrap();
assert_eq!(res, Counter(0));
vm.call_function_by_name_with_args(&push_eval_fn_name(&[push.index()]), vec![])
.unwrap();
let res = node::state::extract::<Counter>(&vm, &[counter.index()])
.unwrap()
.unwrap();
assert_eq!(res, Counter(1));
}
#[test]
fn test_graph_with_counters() {
let mut g = petgraph::graph::DiGraph::new();
let push_a = node_push();
let push_b = node_push();
let push_c = node_push();
let p_a = g.add_node(Box::new(push_a) as Box<dyn DebugNode>);
let p_b = g.add_node(Box::new(push_b) as Box<_>);
let p_c = g.add_node(Box::new(push_c) as Box<_>);
let c_a = g.add_node(Box::new(node_counter()) as Box<_>);
let c_b = g.add_node(Box::new(node_counter()) as Box<_>);
let c_c = g.add_node(Box::new(node_counter()) as Box<_>);
g.add_edge(p_a, c_a, Edge::from((0, 0)));
g.add_edge(c_a, c_b, Edge::from((0, 0)));
g.add_edge(p_b, c_b, Edge::from((0, 0)));
g.add_edge(c_b, c_c, Edge::from((0, 0)));
g.add_edge(p_c, c_c, Edge::from((0, 0)));
let module = gantz_core::compile::module(&g);
let mut vm = Engine::new_base();
vm.register_value(ROOT_STATE, SteelVal::empty_hashmap());
gantz_core::graph::register(&g, &[], &mut vm);
for f in &module {
vm.run(format!("{f}")).unwrap();
}
vm.call_function_by_name_with_args(&push_eval_fn_name(&[p_a.index()]), vec![])
.unwrap();
vm.call_function_by_name_with_args(&push_eval_fn_name(&[p_b.index()]), vec![])
.unwrap();
vm.call_function_by_name_with_args(&push_eval_fn_name(&[p_c.index()]), vec![])
.unwrap();
let a = node::state::extract::<Counter>(&vm, &[c_a.index()])
.unwrap()
.unwrap();
let b = node::state::extract::<Counter>(&vm, &[c_b.index()])
.unwrap()
.unwrap();
let c = node::state::extract::<Counter>(&vm, &[c_c.index()])
.unwrap()
.unwrap();
assert_eq!([a, b, c], [Counter(1), Counter(2), Counter(3)]);
}