1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#![allow(clippy::missing_errors_doc)]

use std::future::Future;

use mlua::prelude::*;

/**
    Utility struct for building Lua tables.
*/
pub struct TableBuilder<'lua> {
    lua: &'lua Lua,
    tab: LuaTable<'lua>,
}

impl<'lua> TableBuilder<'lua> {
    /**
        Creates a new table builder.
    */
    pub fn new(lua: &'lua Lua) -> LuaResult<Self> {
        let tab = lua.create_table()?;
        Ok(Self { lua, tab })
    }

    /**
        Adds a new key-value pair to the table.

        This will overwrite any value that already exists.
    */
    pub fn with_value<K, V>(self, key: K, value: V) -> LuaResult<Self>
    where
        K: IntoLua<'lua>,
        V: IntoLua<'lua>,
    {
        self.tab.raw_set(key, value)?;
        Ok(self)
    }

    /**
        Adds multiple key-value pairs to the table.

        This will overwrite any values that already exist.
    */
    pub fn with_values<K, V>(self, values: Vec<(K, V)>) -> LuaResult<Self>
    where
        K: IntoLua<'lua>,
        V: IntoLua<'lua>,
    {
        for (key, value) in values {
            self.tab.raw_set(key, value)?;
        }
        Ok(self)
    }

    /**
        Adds a new key-value pair to the sequential (array) section of the table.

        This will not overwrite any value that already exists,
        instead adding the value to the end of the array.
    */
    pub fn with_sequential_value<V>(self, value: V) -> LuaResult<Self>
    where
        V: IntoLua<'lua>,
    {
        self.tab.raw_push(value)?;
        Ok(self)
    }

    /**
        Adds multiple values to the sequential (array) section of the table.

        This will not overwrite any values that already exist,
        instead adding the values to the end of the array.
    */
    pub fn with_sequential_values<V>(self, values: Vec<V>) -> LuaResult<Self>
    where
        V: IntoLua<'lua>,
    {
        for value in values {
            self.tab.raw_push(value)?;
        }
        Ok(self)
    }

    /**
        Adds a new key-value pair to the table, with a function value.

        This will overwrite any value that already exists.
    */
    pub fn with_function<K, A, R, F>(self, key: K, func: F) -> LuaResult<Self>
    where
        K: IntoLua<'lua>,
        A: FromLuaMulti<'lua>,
        R: IntoLuaMulti<'lua>,
        F: Fn(&'lua Lua, A) -> LuaResult<R> + 'static,
    {
        let f = self.lua.create_function(func)?;
        self.with_value(key, LuaValue::Function(f))
    }

    /**
        Adds a new key-value pair to the table, with an async function value.

        This will overwrite any value that already exists.
    */
    pub fn with_async_function<K, A, R, F, FR>(self, key: K, func: F) -> LuaResult<Self>
    where
        K: IntoLua<'lua>,
        A: FromLuaMulti<'lua>,
        R: IntoLuaMulti<'lua>,
        F: Fn(&'lua Lua, A) -> FR + 'static,
        FR: Future<Output = LuaResult<R>> + 'lua,
    {
        let f = self.lua.create_async_function(func)?;
        self.with_value(key, LuaValue::Function(f))
    }

    /**
        Adds a metatable to the table.

        This will overwrite any metatable that already exists.
    */
    pub fn with_metatable(self, table: LuaTable) -> LuaResult<Self> {
        self.tab.set_metatable(Some(table));
        Ok(self)
    }

    /**
        Builds the table as a read-only table.

        This will prevent any *direct* modifications to the table.
    */
    pub fn build_readonly(self) -> LuaResult<LuaTable<'lua>> {
        self.tab.set_readonly(true);
        Ok(self.tab)
    }

    /**
        Builds the table.
    */
    pub fn build(self) -> LuaResult<LuaTable<'lua>> {
        Ok(self.tab)
    }
}