use std::any::Any;
use std::collections::HashMap;
use super::LuaUserdata;
use super::userdata_trait::{UdValue, UserDataTrait};
pub struct UserDataBuilder<T: 'static> {
value: T,
type_name: &'static str,
field_getters: HashMap<String, Box<dyn Fn(&T) -> UdValue>>,
field_setters: HashMap<String, Box<dyn Fn(&mut T, UdValue) -> Result<(), String>>>,
tostring_fn: Option<Box<dyn Fn(&T) -> String>>,
}
impl<T: 'static> UserDataBuilder<T> {
pub fn new(value: T) -> Self {
UserDataBuilder {
value,
type_name: std::any::type_name::<T>(),
field_getters: HashMap::new(),
field_setters: HashMap::new(),
tostring_fn: None,
}
}
pub fn set_type_name(mut self, name: &'static str) -> Self {
self.type_name = name;
self
}
pub fn add_field_getter<F>(mut self, name: &str, f: F) -> Self
where
F: Fn(&T) -> UdValue + 'static,
{
self.field_getters.insert(name.to_owned(), Box::new(f));
self
}
pub fn add_field_setter<F>(mut self, name: &str, f: F) -> Self
where
F: Fn(&mut T, UdValue) -> Result<(), String> + 'static,
{
self.field_setters.insert(name.to_owned(), Box::new(f));
self
}
pub fn add_field<G, S>(self, name: &str, getter: G, setter: S) -> Self
where
G: Fn(&T) -> UdValue + 'static,
S: Fn(&mut T, UdValue) -> Result<(), String> + 'static,
{
self.add_field_getter(name, getter)
.add_field_setter(name, setter)
}
pub fn set_tostring<F>(mut self, f: F) -> Self
where
F: Fn(&T) -> String + 'static,
{
self.tostring_fn = Some(Box::new(f));
self
}
pub fn build(self, vm: &mut crate::lua_vm::LuaVM) -> crate::LuaResult<crate::LuaValue> {
let configured = ConfiguredUserData {
value: self.value,
type_name: self.type_name,
field_getters: self.field_getters,
field_setters: self.field_setters,
tostring_fn: self.tostring_fn,
};
let ud = LuaUserdata::new(configured);
vm.create_userdata(ud)
}
}
struct ConfiguredUserData<T: 'static> {
value: T,
type_name: &'static str,
field_getters: HashMap<String, Box<dyn Fn(&T) -> UdValue>>,
field_setters: HashMap<String, Box<dyn Fn(&mut T, UdValue) -> Result<(), String>>>,
tostring_fn: Option<Box<dyn Fn(&T) -> String>>,
}
impl<T: 'static> UserDataTrait for ConfiguredUserData<T> {
fn type_name(&self) -> &'static str {
self.type_name
}
fn get_field(&self, key: &str) -> Option<UdValue> {
self.field_getters.get(key).map(|f| f(&self.value))
}
fn set_field(&mut self, key: &str, value: UdValue) -> Option<Result<(), String>> {
if let Some(setter) = self.field_setters.get(key) {
Some(setter(&mut self.value, value))
} else {
None
}
}
fn lua_tostring(&self) -> Option<String> {
self.tostring_fn.as_ref().map(|f| f(&self.value))
}
fn field_names(&self) -> &'static [&'static str] {
&[]
}
fn as_any(&self) -> &dyn Any {
&self.value
}
fn as_any_mut(&mut self) -> &mut dyn Any {
&mut self.value
}
}