<commit_before>//! Utility methods and constructors for Lua classes

use std::default::Default;
use std::rc::Rc;
use rlua::{self, Lua, ToLua, Table, UserData, AnyUserData, Value, Function};
use super::object::Object;
use super::property::Property;

pub type Allocator = Rc<Fn(&Lua) -> rlua::Result<Object>>;
pub type Collector = Rc<Fn(Object)>;
pub type Checker = Rc<Fn(Object) -> bool>;

#[derive(Debug)]
pub struct Class<'lua> {
    table: Table<'lua>
}

#[derive(Clone)]
pub struct ClassState {
    // NOTE That this is missing fields from the C version.
    // They stored in the meta table instead, to not have unsafety.
    // They are fetchable using getters.
    name: String,
    // TODO Move stringly types to metatable only, not have fields and just have setters / getters?
    // FIXME This __needs__ to happen to ensure gc doesn't collect!
    // Have test that tests that hypo ^
    signals_global: Option<String>,
    parent: Option<String>,
    allocator: Option<Allocator>,
    collector: Option<Collector>,
    checker: Option<Checker>,
    instances: u32,
    // TODO^ Down here too?
    /// The global Lua key that corresponds to this class' function.
    /// Stored this way so that it's not tied to Lua lifetime.
    index_miss_handler_global: Option<String>,
    /// The global Lua key that corresponds to this class' function.
    /// Stored this way so that it's not tied to Lua lifetime.
    newindex_miss_handler_global: Option<String>,
}

pub struct ClassBuilder<'lua>{
    lua: &'lua Lua,
    class: Class<'lua>
}

impl <'lua> ClassBuilder<'lua> {
    pub fn method(self, name: String, meth: rlua::Function)
                  -> rlua::Result<Self> {
        let meta = self.class.table.get_metatable()
            .expect("Class had no meta table!");
        meta.set(name, meth)?;
        Ok(self)
    }

    pub fn property(self, prop: Property<'lua>) -> rlua::Result<Self> {
        let properties = self.class.table.get::<_, Table>("properties")?;
        let length = properties.len().unwrap_or(0) + 1;
        properties.set(length, prop)?;
        Ok(self)
    }

    pub fn save_class(mut self, name: &str)
                      -> rlua::Result<Self> {
        self.lua.globals().set(name, self.class.table)?;
        self.class.table = self.lua.globals().get(name)?;
        Ok(self)
    }

    pub fn build(self) -> rlua::Result<Class<'lua>> {
        Ok(self.class)
    }
}

impl <'lua> ToLua<'lua> for Class<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.table.to_lua(lua)
    }
}

impl Default for ClassState {
    fn default() -> Self {
        ClassState {
            name: String::default(),
            signals_global: Option::default(),
            parent: Option::default(),
            allocator: Option::default(),
            collector: Option::default(),
            checker: Option::default(),
            instances: 0,
            index_miss_handler_global: Option::default(),
            newindex_miss_handler_global: Option::default(),

        }
    }
}

impl UserData for ClassState {}

impl <'lua> Class<'lua> {
    pub fn builder(lua: &'lua Lua,
               allocator: Option<Allocator>,
               collector: Option<Collector>,
               checker: Option<Checker>)
               -> rlua::Result<ClassBuilder<'lua>> {
        let mut class = ClassState::default();
        class.allocator = allocator;
        class.collector = collector;
        class.checker = checker;
        let table = lua.create_table();
        // Store in not meta table so we can't index it
        table.set("data", class)?;
        table.set("properties", Vec::<Property>::new().to_lua(lua)?)?;
        let meta = lua.create_table();
        meta.set("signals", lua.create_table())?;
        meta.set("set_index_miss_handler",
                 lua.create_function(set_index_miss_handler).bind(table.clone())?)?;
        meta.set("set_newindex_miss_handler",
                 lua.create_function(set_newindex_miss_handler).bind(table.clone())?)?;
        meta.set("__index", meta.clone())?;
        table.set_metatable(Some(meta));
        Ok(ClassBuilder{
            lua: lua,
            class: Class { table }
        })
    }

    #[allow(dead_code)]
    pub fn properties(&self) -> rlua::Result<Table<'lua>> {
        self.table.get("properties")
    }

    pub fn checker(&self) -> rlua::Result<Option<Checker>> {
        self.table.get::<_, ClassState>("data")
            .map(|state| state.checker)
    }
}

impl <'lua> From<Table<'lua>> for Class<'lua> {
    fn from(table: Table<'lua>) -> Self {
        Class { table }
    }
}

fn set_index_miss_handler<'lua>(_: &'lua Lua, (obj, func): (Table, Function))
                                -> rlua::Result<()> {
    let meta = obj.get_metatable()
        .expect("Object had no metatable");
    meta.set("__index_miss_handler", func)?;
    Ok(())
}
fn set_newindex_miss_handler<'lua>(_: &'lua Lua, (obj, func): (Table, Function))
                                   -> rlua::Result<()> {
    let meta = obj.get_metatable()
        .expect("Object had no metatable");
    meta.set("__newindex_miss_handler", func)?;
    Ok(())
}

pub fn button_class(lua: &Lua) -> rlua::Result<Class> {
    let table = lua.globals().get::<_, Table>("button")
        .expect("Button class was not set! Did you call button::init?");
    assert!(table.get::<_, AnyUserData>("data")?.is::<ClassState>(),
            "This table was not a class!");
    Ok(Class { table })
}<commit_msg>Removed miss_handler fileds, fixed small things<commit_after>//! Utility methods and constructors for Lua classes

use std::default::Default;
use std::rc::Rc;
use rlua::{self, Lua, ToLua, Table, UserData, AnyUserData, Value, Function};
use super::object::Object;
use super::property::Property;

pub type Allocator = Rc<Fn(&Lua) -> rlua::Result<Object>>;
pub type Collector = Rc<Fn(Object)>;
pub type Checker = Rc<Fn(Object) -> bool>;

#[derive(Debug)]
pub struct Class<'lua> {
    table: Table<'lua>
}

#[derive(Clone)]
pub struct ClassState {
    // NOTE That this is missing fields from the C version.
    // They stored in the meta table instead, to not have unsafety.
    // They are fetchable using getters.
    name: String,
    // TODO Move stringly types to metatable only, not have fields and just have setters / getters?
    // FIXME This __needs__ to happen to ensure gc doesn't collect!
    // Have test that tests that hypo ^
    signals_global: Option<String>,
    parent: Option<String>,
    allocator: Option<Allocator>,
    collector: Option<Collector>,
    checker: Option<Checker>,
    instances: u32
}

pub struct ClassBuilder<'lua>{
    lua: &'lua Lua,
    class: Class<'lua>
}

impl <'lua> ClassBuilder<'lua> {
    pub fn method(self, name: String, meth: rlua::Function)
                  -> rlua::Result<Self> {
        let meta = self.class.table.get_metatable()
            .expect("Class had no meta table!");
        meta.set(name, meth)?;
        Ok(self)
    }

    pub fn property(self, prop: Property<'lua>) -> rlua::Result<Self> {
        let properties = self.class.table.get::<_, Table>("properties")?;
        let length = properties.len().unwrap_or(0) + 1;
        properties.set(length, prop)?;
        Ok(self)
    }

    pub fn save_class(mut self, name: &str)
                      -> rlua::Result<Self> {
        self.lua.globals().set(name, self.class.table)?;
        self.class.table = self.lua.globals().get(name)?;
        Ok(self)
    }

    pub fn build(self) -> rlua::Result<Class<'lua>> {
        Ok(self.class)
    }
}

impl <'lua> ToLua<'lua> for Class<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.table.to_lua(lua)
    }
}

impl Default for ClassState {
    fn default() -> Self {
        ClassState {
            name: String::default(),
            signals_global: Option::default(),
            parent: Option::default(),
            allocator: Option::default(),
            collector: Option::default(),
            checker: Option::default(),
            instances: 0
        }
    }
}

impl UserData for ClassState {}

impl <'lua> Class<'lua> {
    pub fn builder(lua: &'lua Lua,
                   allocator: Option<Allocator>,
                   collector: Option<Collector>,
                   checker: Option<Checker>)
                   -> rlua::Result<ClassBuilder<'lua>> {
        let mut class = ClassState::default();
        class.allocator = allocator;
        class.collector = collector;
        class.checker = checker;
        let table = lua.create_table();
        // Store in not meta table so we can't index it
        table.set("data", class)?;
        table.set("properties", Vec::<Property>::new().to_lua(lua)?)?;
        let meta = lua.create_table();
        meta.set("signals", lua.create_table())?;
        meta.set("set_index_miss_handler",
                 lua.create_function(set_index_miss_handler).bind(table.clone())?)?;
        meta.set("set_newindex_miss_handler",
                 lua.create_function(set_newindex_miss_handler).bind(table.clone())?)?;
        meta.set("__index", meta.clone())?;
        table.set_metatable(Some(meta));
        Ok(ClassBuilder{
            lua: lua,
            class: Class { table }
        })
    }

    #[allow(dead_code)]
    pub fn properties(&self) -> rlua::Result<Table<'lua>> {
        self.table.get("properties")
    }

    pub fn checker(&self) -> rlua::Result<Option<Checker>> {
        self.table.get::<_, ClassState>("data")
            .map(|state| state.checker)
    }
}

impl <'lua> From<Table<'lua>> for Class<'lua> {
    fn from(table: Table<'lua>) -> Self {
        Class { table }
    }
}

fn set_index_miss_handler<'lua>(_: &'lua Lua, (class, func): (Table, Function))
                                -> rlua::Result<()> {
    let meta = class.get_metatable()
        .expect("Object had no metatable");
    meta.set("__index_miss_handler", func)?;
    Ok(())
}
fn set_newindex_miss_handler<'lua>(_: &'lua Lua, (class, func): (Table, Function))
                                   -> rlua::Result<()> {
    let meta = class.get_metatable()
        .expect("Object had no metatable");
    meta.set("__newindex_miss_handler", func)?;
    Ok(())
}

pub fn button_class(lua: &Lua) -> rlua::Result<Class> {
    let table = lua.globals().get::<_, Table>("button")
        .expect("Button class was not set! Did you call button::init?");
    assert!(table.get::<_, AnyUserData>("data")?.is::<ClassState>(),
            "This table was not a class!");
    Ok(Class { table })
}
{'fraction_non_alphanumeric': 0.1517156631061537, 'fraction_numerical': 0.0009452689290103034, 'mean_word_length': 2.59375, 'pattern_counts': {'":': 0, '<': 111, '<?xml version=': 0, '>': 141, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '471429', 'n_tokens_mistral': 3273, 'n_tokens_neox': 3316, 'n_words': 805}