rustsim_core/collect.rs
1//! Data collection utilities.
2//!
3//! Mirrors Julia Agents.jl `run!` data collection: at each step, agent-level
4//! and model-level reporter closures produce rows that are appended to
5//! user-provided vectors.
6
7#![allow(clippy::type_complexity)]
8
9use crate::{agent::Agent, model::Model, types::AgentId};
10
11/// Collect one step's worth of agent and model data.
12///
13/// For each agent ID in `agent_ids`, if the agent exists and `agent_report`
14/// is `Some`, the reporter closure is called and the result appended to
15/// `agent_data`. If `model_report` is `Some`, one model-level row is appended
16/// to `model_data`.
17///
18/// # Arguments
19///
20/// - `model` - the model to collect from.
21/// - `agent_ids` - the agent IDs to report on (typically from [`Model::agent`] iteration).
22/// - `agent_report` - optional closure `(&Agent, &Model) -> AgentRow`.
23/// - `model_report` - optional closure `(&Model) -> ModelRow`.
24/// - `agent_data` - accumulator for agent-level rows.
25/// - `model_data` - accumulator for model-level rows.
26///
27/// [`Model::agent`]: crate::model::Model::agent
28pub fn collect_step<A, M, AgentRow, ModelRow>(
29 model: &M,
30 agent_ids: &[AgentId],
31 agent_report: Option<&dyn Fn(&A, &M) -> AgentRow>,
32 model_report: Option<&dyn Fn(&M) -> ModelRow>,
33 agent_data: &mut Vec<AgentRow>,
34 model_data: &mut Vec<ModelRow>,
35) where
36 A: Agent,
37 M: Model<Agent = A>,
38{
39 if let Some(report) = agent_report {
40 for &id in agent_ids {
41 if let Some(agent) = model.agent(id) {
42 agent_data.push(report(&agent, model));
43 }
44 }
45 }
46 if let Some(report) = model_report {
47 model_data.push(report(model));
48 }
49}