use std::sync::Arc;
use mlua::{Lua, Result, Table};
use crate::application::context::{BindingContext, resolve_substrate};
use crate::domain::config::Config;
use crate::domain::event::ExperimentEvent;
pub fn register(lua: &Lua, dstest: &Table, ctx: &BindingContext) -> Result<()> {
let state = Arc::clone(ctx.state());
let resolver = Arc::clone(ctx.resolver());
let substrate_slot = Arc::clone(ctx.substrate_slot());
let workload_rng = Arc::clone(ctx.workload_rng());
let config_fn = lua.create_function(move |lua, tbl: Table| {
let state = Arc::clone(&state);
let resolver = Arc::clone(&resolver);
let substrate_slot = Arc::clone(&substrate_slot);
let workload_rng = Arc::clone(&workload_rng);
let mut cfg = Config::default();
let name: Option<String> = tbl.get("name").ok();
let resolved = match tbl.get::<String>("substrate") {
Ok(s) => {
let resolved = resolve_substrate(&resolver, &substrate_slot, &s)?;
cfg.substrate = Some(s);
Some(resolved)
}
Err(_) => None,
};
if let Ok(seed) = tbl.get::<u64>("seed") {
cfg.seed = Some(seed);
let globals = lua.globals();
let math: Table = globals.get("math")?;
let randomseed: mlua::Function = math.get("randomseed")?;
randomseed.call::<()>(seed)?;
if let Some(substrate) = &resolved {
substrate.network().set_seed(seed);
substrate.storage().set_seed(seed);
}
*workload_rng.lock().expect("poisoned rng lock") =
Some(rand::SeedableRng::seed_from_u64(seed));
}
if let Ok(weights) = tbl.get::<Table>("weights") {
let mut fault_weights = std::collections::BTreeMap::new();
for (k, v) in weights.pairs::<String, f32>().flatten() {
fault_weights.insert(k, v);
}
cfg.fault_weights = fault_weights;
}
if let Ok(mode) = tbl.get::<String>("accumulation") {
cfg.accumulation_mode = mode
.parse()
.map_err(|e: String| mlua::Error::RuntimeError(e))?;
}
if let Ok(timeout) = tbl.get::<u64>("http_timeout") {
cfg.http_timeout_secs = timeout;
}
if let Ok(retries) = tbl.get::<u32>("http_retries") {
cfg.http_retries = retries;
}
if let Ok(delay) = tbl.get::<u64>("http_retry_delay") {
cfg.http_retry_delay_ms = delay;
}
if let Ok(delay) = tbl.get::<u64>("step_delay") {
cfg.step_delay_ms = delay;
}
if let Ok(steps) = tbl.get::<usize>("steps") {
cfg.steps = steps;
}
if let Ok(require) = tbl.get::<bool>("require_seed") {
cfg.require_seed = require;
}
if cfg.substrate.is_none() {
return Err(mlua::Error::RuntimeError(
"dstest.config requires a `substrate` field".to_string(),
));
}
cfg.validate()
.map_err(|e| mlua::Error::RuntimeError(format!("invalid configuration: {}", e)))?;
cfg.normalize_weights();
let mut state = state.lock().expect("poisoned engine state lock");
let handle = match name {
Some(n) => n,
None => state.configs.unique_handle(),
};
if state.configs.contains(&handle) {
return Err(mlua::Error::RuntimeError(format!(
"config '{}' already exists",
handle
)));
}
state.configs.register(handle.clone(), cfg);
state.log.push(ExperimentEvent::ConfigRegistered);
Ok(handle)
})?;
dstest.set("config", config_fn)?;
Ok(())
}