use rlua::prelude::*;use rlua::{UserDataMethods, UserData, MetaMethod, Value, Table, Lua, FromLua};use std::collections::HashSet;#[derive(Clone)]struct StringSet (HashSet<String>);impl UserData for StringSet {    fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {        methods.add_method_mut("insert", |_, this, elem: String| {            this.0.insert(elem);            Ok(())        });        methods.add_method_mut("remove", |_, this, elem: String| {            this.0.remove(&elem);            Ok(())        });        methods.add_method("contains", |_, this, elem: String| {            Ok(this.0.contains(&elem))        });        methods.add_method_mut("clear", |_, this, _: ()| {            this.0.clear();            Ok(())        });        methods.add_method("is_empty", |_, this, _: ()| {            Ok(this.0.is_empty())        });        methods.add_method("difference", |_, this, other: StringSet| {            let result: HashSet<String> = this.0.difference(&other.0).cloned().collect();            Ok(StringSet(result))        });        methods.add_method("symmetric", |_, this, other: StringSet| {            let result: HashSet<String> = this.0.symmetric_difference(&other.0).cloned().collect();            Ok(StringSet(result))        });        methods.add_method("intersection", |_, this, other: StringSet| {            let result: HashSet<String> = this.0.intersection(&other.0).cloned().collect();            Ok(StringSet(result))        });        methods.add_method("union", |_, this, other: StringSet| {            let result: HashSet<String> = this.0.union(&other.0).cloned().collect();            Ok(StringSet(result))        });        methods.add_method("is_disjoint", |_, this, other: StringSet| {            Ok(this.0.is_disjoint(&other.0))        });        methods.add_method("is_subset", |_, this, other: StringSet| {            Ok(this.0.is_subset(&other.0))        });        methods.add_method("is_superset", |_, this, other: StringSet| {            Ok(this.0.is_superset(&other.0))        });        methods.add_meta_method(MetaMethod::ToString, |_, this, _: ()| {            Ok(format!("{:?}", this.0))        });        methods.add_meta_method(MetaMethod::Len, |_, this, _: ()| {            Ok(this.0.len())        });        methods.add_method("clone", |_, this, _: ()| {            Ok(StringSet(this.0.clone()))        });        methods.add_method("to_table", |lua, this, _: ()| {            let table = lua.create_sequence_from(this.0.iter().cloned())?;            Ok(table)        });    }}pub fn init(lua: &Lua) -> Result<(), LuaError> {    type Set = HashSet<String>;    fn from_table (table: Table) -> Result<Set, LuaError> {        let mut set = HashSet::new();        for elem in table.sequence_values() {            set.insert(elem?);        }        Ok(set)    }    fn get_sets (lua: &Lua, args: (Value, Value)) -> Result<(Set, Set), LuaError> {        fn from_value (a: Value, lua: &Lua) -> Result<Set, LuaError> {            Ok(match a {                Value::Table(t) => from_table(t)?,                a@_ => StringSet::from_lua(a, lua)?.0            })        }        let (a, b) = args;        Ok((from_value(a, lua)?, from_value(b, lua)?))    }    let module = lua.create_table()?;    module.set("create", lua.create_function(        |_, _: ()|  Ok(StringSet(HashSet::new()))    )? )?;    module.set("from_table", lua.create_function(        |_, t: Table|  Ok(StringSet(from_table(t)?))    )? )?;    let g = lua.globals();    g.set("stringset", module)?;    g.set("difference", lua.create_function( |lua, args: (Value, Value)| {        let (a, b) = get_sets(lua, args)?;        let c = a.difference(&b).cloned().collect();        Ok(StringSet(c))    })? )?;    g.set("symmetric", lua.create_function( |lua, args: (Value, Value)| {        let (a, b) = get_sets(lua, args)?;        let c = a.symmetric_difference(&b).cloned().collect();        Ok(StringSet(c))    })? )?;    g.set("intersection", lua.create_function( |lua, args: (Value, Value)| {        let (a, b) = get_sets(lua, args)?;        let c = a.intersection(&b).cloned().collect();        Ok(StringSet(c))    })? )?;    g.set("union", lua.create_function( |lua, args: (Value, Value)| {        let (a, b) = get_sets(lua, args)?;        let c = a.union(&b).cloned().collect();        Ok(StringSet(c))    })? )?;    Ok(())}#[cfg(test)]mod tests {    use super::*;    #[test]    fn direct_methods() {        let lua = Lua::new();        init(&lua).unwrap();        lua.exec::<_, Value>(r#"            local a = stringset.create()            a:insert("Colombia")            a:insert("Canada")            a:insert("China")            local b = stringset.create()            b:insert("Venezuela")            b:insert("Colombia")            b:insert("Brazil")            local c = a:union(b)            assert(#c == 5)            assert(c:contains("Colombia"))            assert(c:contains("Venezuela"))            assert(c:contains("Canada"))            c = a:intersection(b)            assert(#c == 1)            assert(c:contains("Colombia"))            assert(not c:contains("Canada"))            c = a:difference(b)            assert(#c == 2)            assert(c:contains("Canada"))            assert(not c:contains("Colombia"))            c = a:symmetric(b)            assert(#c == 4)            assert(c:contains("Canada"))            assert(c:contains("Venezuela"))            assert(not c:contains("Colombia"))            d = a:clone()            d:remove("Canada")            assert(a:is_superset(d))            assert(d:is_subset(a))            assert(not a:is_disjoint(b))            assert(a:is_disjoint(b:difference(a)))            d:clear()            assert(d:is_empty())            local t = a:union(b):to_table()            for i, v in ipairs(t) do                print(i, v)            end        "#, None).unwrap();    }    #[test]    fn shortcut_syntax() {        let lua = Lua::new();        init(&lua).unwrap();        lua.exec::<_, Value>(r#"            local a = stringset.create()            a:insert("Canada")            a:insert("China")            a:insert("Colombia")            local b = {"Colombia", "Brazil", "Venezuela"}            local c = intersection(a, b)            assert(c:contains("Colombia"))            assert(not c:contains("Canada"))            union(a, b)            difference(a, b)            symmetric(a, b)        "#, None).unwrap();    }}