rustsim-core 0.0.1

Core ABM engine: agents, models, stores, schedulers, stepping, data collection
Documentation
//! Data collection utilities.
//!
//! Mirrors Julia Agents.jl `run!` data collection: at each step, agent-level
//! and model-level reporter closures produce rows that are appended to
//! user-provided vectors.

#![allow(clippy::type_complexity)]

use crate::{agent::Agent, model::Model, types::AgentId};

/// Collect one step's worth of agent and model data.
///
/// For each agent ID in `agent_ids`, if the agent exists and `agent_report`
/// is `Some`, the reporter closure is called and the result appended to
/// `agent_data`. If `model_report` is `Some`, one model-level row is appended
/// to `model_data`.
///
/// # Arguments
///
/// - `model` - the model to collect from.
/// - `agent_ids` - the agent IDs to report on (typically from [`Model::agent`] iteration).
/// - `agent_report` - optional closure `(&Agent, &Model) -> AgentRow`.
/// - `model_report` - optional closure `(&Model) -> ModelRow`.
/// - `agent_data` - accumulator for agent-level rows.
/// - `model_data` - accumulator for model-level rows.
///
/// [`Model::agent`]: crate::model::Model::agent
pub fn collect_step<A, M, AgentRow, ModelRow>(
    model: &M,
    agent_ids: &[AgentId],
    agent_report: Option<&dyn Fn(&A, &M) -> AgentRow>,
    model_report: Option<&dyn Fn(&M) -> ModelRow>,
    agent_data: &mut Vec<AgentRow>,
    model_data: &mut Vec<ModelRow>,
) where
    A: Agent,
    M: Model<Agent = A>,
{
    if let Some(report) = agent_report {
        for &id in agent_ids {
            if let Some(agent) = model.agent(id) {
                agent_data.push(report(&agent, model));
            }
        }
    }
    if let Some(report) = model_report {
        model_data.push(report(model));
    }
}