2026-07-06 00:41:38 -05:00
|
|
|
use rhai::{Dynamic, Engine, ImmutableString};
|
|
|
|
|
use crate::api::board::BoardRef;
|
|
|
|
|
use crate::script::Registerable;
|
2026-07-10 23:16:28 -05:00
|
|
|
use crate::utils::{LogSink, RegistryValue};
|
2026-07-06 00:41:38 -05:00
|
|
|
|
|
|
|
|
/// The board's script registry, pushed into scope as the constant `Registry`.
|
|
|
|
|
/// `get`/`set`/`get_or` methods let scripts read and write per-board key→value pairs
|
|
|
|
|
/// that persist across board transitions. Mutation goes through the inner
|
|
|
|
|
/// `Rc<RefCell<Board>>`, so the struct itself can be shared as a scope constant.
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct Registry(pub BoardRef);
|
|
|
|
|
|
|
|
|
|
impl Registerable for Registry {
|
2026-07-10 23:16:28 -05:00
|
|
|
fn register(engine: &mut Engine, _log_sink: LogSink) {
|
2026-07-06 00:41:38 -05:00
|
|
|
engine.register_type_with_name::<Registry>("Registry");
|
|
|
|
|
|
|
|
|
|
// Registry.get(key) -> Dynamic — returns () if the key is absent.
|
|
|
|
|
engine.register_fn("get", |r: &mut Registry, key: ImmutableString| -> Dynamic {
|
|
|
|
|
r.0
|
|
|
|
|
.borrow()
|
|
|
|
|
.registry
|
|
|
|
|
.get(key.as_str())
|
|
|
|
|
.map(|v| v.clone().into())
|
|
|
|
|
.unwrap_or(Dynamic::UNIT)
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Registry.set(key, value) — stores a primitive value; () removes the key;
|
|
|
|
|
// unsupported types (closures, custom objects) are silently ignored.
|
|
|
|
|
engine.register_fn(
|
|
|
|
|
"set",
|
|
|
|
|
|r: &mut Registry, key: ImmutableString, value: Dynamic| {
|
|
|
|
|
match RegistryValue::try_from(value.clone()) {
|
|
|
|
|
Ok(rv) => {
|
|
|
|
|
r.0.borrow_mut().registry.insert(key.to_string(), rv);
|
|
|
|
|
}
|
|
|
|
|
Err(()) if value.is_unit() => {
|
|
|
|
|
r.0.borrow_mut().registry.remove(key.as_str());
|
|
|
|
|
}
|
|
|
|
|
Err(()) => {} // unsupported type — silently ignore
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Registry.get_or(key, default) — returns the stored value when present and the
|
|
|
|
|
// same Rhai type as `default`; falls back to `default` otherwise.
|
|
|
|
|
engine.register_fn(
|
|
|
|
|
"get_or",
|
|
|
|
|
|r: &mut Registry, key: ImmutableString, default: Dynamic| -> Dynamic {
|
|
|
|
|
if let Some(stored) = r.0.borrow().registry.get(key.as_str()).cloned() {
|
|
|
|
|
let candidate: Dynamic = stored.into();
|
|
|
|
|
// Only return the stored value if it matches the type of the default.
|
|
|
|
|
if candidate.type_id() == default.type_id() {
|
|
|
|
|
candidate
|
|
|
|
|
} else {
|
|
|
|
|
default
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
default
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|