use std::{collections::HashMap, fmt::Debug};
use crate::{RuntimeError, Value, ValueRef};
type Method<T> = fn(&mut T, &[ValueRef]) -> Result<Option<ValueRef>, RuntimeError>;
type Getter<T> = fn(&T) -> Result<Value, RuntimeError>;
type Setter<T> = fn(&mut T, Value) -> Result<(), RuntimeError>;
#[derive(Debug)]
pub struct MetaTable<T> {
pub name: String,
pub methods: HashMap<String, Method<T>>,
pub properties: HashMap<String, MetaProperty<T>>,
}
impl<T> MetaTable<T> {
pub fn new(name: impl ToString) -> Self {
Self {
name: name.to_string(),
methods: HashMap::new(),
properties: HashMap::new(),
}
}
pub fn with_method(mut self, name: impl ToString, method: Method<T>) -> Self {
self.methods.insert(name.to_string(), method);
self
}
pub fn add_property(&mut self, property: MetaProperty<T>) {
self.properties.insert(property.name.clone(), property);
}
pub fn with_property_getter(mut self, name: &str, getter: Getter<T>) -> Self {
self.properties.insert(
name.to_string(),
MetaProperty::new(name, Some(getter), None),
);
self
}
pub fn with_property_setter(mut self, name: &str, setter: Setter<T>) -> Self {
self.properties.insert(
name.to_string(),
MetaProperty::new(name, None, Some(setter)),
);
self
}
pub fn property_get(&self, this: &T, name: &str) -> Result<Value, RuntimeError> {
match self.properties.get(name) {
Some(property) => match property.getter {
Some(getter) => getter(this),
None => Err(RuntimeError::missing_property_getter::<T>(name)),
},
None => Err(RuntimeError::missing_property::<T>(name)),
}
}
pub fn property_set(&self, this: &mut T, name: &str, value: Value) -> Result<(), RuntimeError> {
match self.properties.get(name) {
Some(property) => match property.setter {
Some(setter) => setter(this, value),
None => Err(RuntimeError::missing_property_setter::<T>(name)),
},
None => Err(RuntimeError::missing_property::<T>(name)),
}
}
pub fn call_method(
&self,
this: &mut T,
method: &str,
args: &[ValueRef],
) -> Result<Option<ValueRef>, RuntimeError> {
match self.methods.get(method) {
Some(method_fn) => (method_fn)(this, args),
None => Err(RuntimeError::missing_method::<T>(method)),
}
}
}
#[derive(Debug)]
pub struct MetaProperty<T> {
pub name: String,
pub getter: Option<Getter<T>>,
pub setter: Option<Setter<T>>,
}
impl<T> MetaProperty<T> {
pub fn new(name: &str, getter: Option<Getter<T>>, setter: Option<Setter<T>>) -> Self {
Self {
name: name.to_string(),
getter,
setter,
}
}
}