From 96cb7e26cd94cf7fc81bf04f5e72fa308fbf4786 Mon Sep 17 00:00:00 2001 From: Robert Jacobson Date: Mon, 25 May 2026 15:38:15 -0500 Subject: [PATCH] feat: `schedule_relative!` macro --- .../ixa-runner-tests/tests/macros.rs | 74 +++++++++++++++++++ src/macros/mod.rs | 1 + src/macros/schedule_relative.rs | 32 ++++++++ src/prelude.rs | 2 +- 4 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 src/macros/schedule_relative.rs diff --git a/integration-tests/ixa-runner-tests/tests/macros.rs b/integration-tests/ixa-runner-tests/tests/macros.rs index f3df75c3..0bb21d90 100644 --- a/integration-tests/ixa-runner-tests/tests/macros.rs +++ b/integration-tests/ixa-runner-tests/tests/macros.rs @@ -1,6 +1,8 @@ #[cfg(test)] mod tests { + use std::cell::RefCell; use std::hash::{Hash, Hasher}; + use std::rc::Rc; use ixa::prelude::*; use ixa::HashSet; @@ -138,6 +140,26 @@ mod tests { // Test rng macro define_rng!(TestRngId); + fn record_sum( + context: &mut Context, + records: Rc>>, + arg1: u32, + arg2: u32, + arg3: u32, + ) { + records + .borrow_mut() + .push((context.get_current_time(), arg1 + arg2 + arg3)); + } + + fn record_current_time(context: &mut Context, records: Rc>>) { + records.borrow_mut().push(context.get_current_time()); + } + + fn passthrough_context(context: &mut Context) -> &mut Context { + context + } + #[test] fn compile_and_run_macros() { let mut ctx = Context::new(); @@ -238,4 +260,56 @@ mod tests { let b = 1.0f64 + 1e-12; ixa::assert_almost_eq!(a, b, 1e-10); } + + #[test] + fn schedule_relative_accepts_function_name_with_arguments() { + let mut context = Context::new(); + let records = Rc::new(RefCell::new(Vec::new())); + let observed_records = records.clone(); + + schedule_relative!(&mut context, 2.5, record_sum, records, 1, 2, 3); + + context.execute(); + assert_eq!(*observed_records.borrow(), vec![(2.5, 6)]); + } + + #[test] + fn schedule_relative_accepts_closure_action() { + let mut context = Context::new(); + let records = Rc::new(RefCell::new(Vec::new())); + let observed_records = records.clone(); + + schedule_relative!( + &mut context, + 1.25, + |context: &mut Context, records: Rc>>, value: u32| { + records + .borrow_mut() + .push((context.get_current_time(), value)); + }, + records, + 42 + ); + + context.execute(); + assert_eq!(*observed_records.borrow(), vec![(1.25, 42)]); + } + + #[test] + fn schedule_relative_accepts_context_expression_and_no_action_arguments() { + let mut context = Context::new(); + let records = Rc::new(RefCell::new(Vec::new())); + let observed_records = records.clone(); + + schedule_relative!( + passthrough_context(&mut context), + 0.75, + record_current_time, + records + ); + schedule_relative!(passthrough_context(&mut context), 1.5, Context::shutdown); + + context.execute(); + assert_eq!(*observed_records.borrow(), vec![0.75]); + } } diff --git a/src/macros/mod.rs b/src/macros/mod.rs index f1c3ddc1..e14a9227 100644 --- a/src/macros/mod.rs +++ b/src/macros/mod.rs @@ -6,4 +6,5 @@ mod define_rng; mod edge_impl; mod entity_impl; mod property_impl; +mod schedule_relative; mod with; diff --git a/src/macros/schedule_relative.rs b/src/macros/schedule_relative.rs new file mode 100644 index 00000000..04086d16 --- /dev/null +++ b/src/macros/schedule_relative.rs @@ -0,0 +1,32 @@ +/// Schedules an action after a delay relative to the context's current time. +/// +/// The scheduled action is called with the executing `context: &mut Context` as +/// its first argument, followed by each remaining macro argument in the same +/// order. +/// +/// For example: +/// +/// ```ignore +/// schedule_relative!(context, my_delay, my_handler, arg1, arg2, arg3); +/// ``` +/// +/// expands to code like: +/// +/// ```ignore +/// { +/// let time = context.get_current_time() + my_delay; +/// context.add_plan(time, move |context| { +/// my_handler(context, arg1, arg2, arg3) +/// }) +/// } +/// ``` +#[macro_export] +macro_rules! schedule_relative { + ($context:expr, $delay:expr, $action:expr $(, $arg:expr)* $(,)?) => {{ + let context = ($context); + let time = context.get_current_time() + $delay; + context.add_plan(time, move |context| { + ($action)(context $(, $arg)*) + }) + }}; +} diff --git a/src/prelude.rs b/src/prelude.rs index 7e202a85..5f25a85b 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -11,5 +11,5 @@ pub use crate::report::ContextReportExt; pub use crate::{ define_data_plugin, define_derived_property, define_edge_type, define_entity, define_global_property, define_multi_property, define_property, define_report, define_rng, - impl_edge_type, impl_entity, impl_property, with, PluginContext, + impl_edge_type, impl_entity, impl_property, schedule_relative, with, PluginContext, };