use state_macro::{stateful, with_state};
use std::cell::RefCell;
use std::rc::Rc;
type State<T> = Rc<RefCell<T>>;
pub struct TestState {
counter: i32,
values: Vec<i32>,
}
impl TestState {
fn new() -> Self {
TestState {
counter: 0,
values: Vec::new(),
}
}
fn increment(&mut self) -> i32 {
self.counter += 1;
self.counter
}
fn add_value(&mut self, value: i32) {
self.values.push(value);
}
}
fn param(state: &State<TestState>) -> i32 {
state.borrow_mut().increment()
}
fn store(state: &State<TestState>, value: i32) {
state.borrow_mut().add_value(value);
}
fn get(state: &State<TestState>) -> &State<TestState> {
state
}
pub mod foo {
use super::*;
pub fn bar(state: &State<TestState>, x: i32) -> i32 {
state.borrow_mut().increment();
x * 2
}
pub fn baz() -> i32 {
42
}
}
#[test]
fn test_with_state_basic() {
let state = Rc::new(RefCell::new(TestState::new()));
with_state! { &state;
let x = ::param();
let y = ::param();
::store(x + y);
}
assert_eq!(state.borrow().counter, 2);
assert_eq!(state.borrow().values, vec![3]);
}
#[test]
fn test_with_state_namespaced_functions() {
let state = Rc::new(RefCell::new(TestState::new()));
with_state! { &state;
let x = ::param();
let y = foo::bar(&state, x);
let z = foo::baz(); ::store(y + z);
}
assert_eq!(state.borrow().counter, 2);
assert_eq!(state.borrow().values, vec![44]);
}
#[test]
fn test_with_state_nested_calls() {
let state = Rc::new(RefCell::new(TestState::new()));
with_state! { &state;
let x = ::param();
let y = ::param() + foo::bar(&state, ::param());
::store(x + y);
}
assert_eq!(state.borrow().counter, 4);
assert_eq!(state.borrow().values, vec![9]);
}
#[test]
fn test_with_state_expression_blocks() {
let state = Rc::new(RefCell::new(TestState::new()));
with_state! { &state;
let x = {
let a = ::param();
let b = ::param();
a + b
};
::store(x);
}
assert_eq!(state.borrow().counter, 2);
assert_eq!(state.borrow().values, vec![3]);
}
#[stateful(&State<TestState>)]
fn process_data(x: i32, y: i32) -> i32 {
let state = ::get();
let a = ::param();
let b = ::param();
let c = foo::bar(state, x); ::store(a + b + c + y);
a + b + c + y
}
#[test]
fn test_stateful_attribute() {
let state = Rc::new(RefCell::new(TestState::new()));
let result = process_data(&state, 10, 20);
assert_eq!(result, 43);
assert_eq!(state.borrow().counter, 3);
assert_eq!(state.borrow().values, vec![43]);
}
#[test]
fn test_multiple_stateful_calls() {
let state = Rc::new(RefCell::new(TestState::new()));
let result1 = process_data(&state, 5, 10);
let result2 = process_data(&state, 15, 25);
assert_eq!(result1, 23);
assert_eq!(result2, 64);
assert_eq!(state.borrow().counter, 6);
assert_eq!(state.borrow().values, vec![23, 64]);
}
#[test]
fn test_string_with_double_colon() {
let state = Rc::new(RefCell::new(TestState::new()));
with_state! { &state;
let _s = "This::should::not::be::transformed"; let x = ::param();
::store(x);
}
assert_eq!(state.borrow().counter, 1);
assert_eq!(state.borrow().values, vec![1]);
}
#[stateful(&State<TestState>)]
fn function_with_state_variable(x: i32) -> i32 {
let state = "this is a local variable";
let state_ref = ::get();
let result = param(state_ref) + x;
assert_eq!(state, "this is a local variable");
result
}
#[test]
fn test_state_param_collision_avoidance() {
let test_state = Rc::new(RefCell::new(TestState::new()));
let result = function_with_state_variable(&test_state, 10);
assert_eq!(result, 11);
assert_eq!(test_state.borrow().counter, 1);
}
#[stateful(&State<TestState>)]
fn function_with_multiple_state_variables(x: i32) -> i32 {
let state = "state variable";
let state1 = "state1 variable";
let state2 = "state2 variable";
let result = ::param() + x;
assert_eq!(state, "state variable");
assert_eq!(state1, "state1 variable");
assert_eq!(state2, "state2 variable");
result
}
#[test]
fn test_complex_state_param_collision() {
let test_state = Rc::new(RefCell::new(TestState::new()));
let result = function_with_multiple_state_variables(&test_state, 20);
assert_eq!(result, 21);
assert_eq!(test_state.borrow().counter, 1);
}