use hashbrown::HashMap; use mlua::{Lua, LuaString, MetaMethod, UserData, UserDataMethods, Value}; pub type ComposerGet = fn(&Lua, &[u8]) -> mlua::Result; pub type ComposerSet = fn(&Lua, &[u8], Value) -> mlua::Result; pub struct Composer { get: G, set: S, cache: HashMap, Value>, } impl Composer where G: Fn(&Lua, &[u8]) -> mlua::Result + 'static, S: Fn(&Lua, &[u8], Value) -> mlua::Result + 'static, { pub fn new(get: G, set: S) -> Self { Self { get, set, cache: Default::default() } } } impl UserData for Composer where G: Fn(&Lua, &[u8]) -> mlua::Result + 'static, S: Fn(&Lua, &[u8], Value) -> mlua::Result + 'static, { fn add_methods>(methods: &mut M) { methods.add_meta_method_mut(MetaMethod::Index, |lua, me, key: LuaString| { let key = key.as_bytes(); if let Some(v) = me.cache.get(key.as_ref()) { return Ok(v.clone()); } let value = (me.get)(lua, &key)?; me.cache.insert(key.to_owned(), value.clone()); Ok(value) }); methods.add_meta_method_mut( MetaMethod::NewIndex, |lua, me, (key, value): (LuaString, Value)| { let key = key.as_bytes(); let value = (me.set)(lua, key.as_ref(), value)?; if value.is_nil() { me.cache.remove(key.as_ref()); } else { me.cache.insert(key.to_owned(), value); } Ok(()) }, ); } }