use rhai::{Dynamic, Engine, ImmutableString}; use crate::api::board::BoardRef; use crate::script::Registerable; use crate::utils::{LogSink, RegistryValue}; /// 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>`, so the struct itself can be shared as a scope constant. #[derive(Clone)] pub struct Registry(pub BoardRef); impl Registerable for Registry { fn register(engine: &mut Engine, _log_sink: LogSink) { engine.register_type_with_name::("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 } }, ); } }