1 module luad.table; 2 3 import luad.c.all; 4 5 import luad.base; 6 import luad.stack; 7 import luad.conversions.structs; 8 9 /// Represents a Lua table. 10 struct LuaTable 11 { 12 /// LuaTable sub-types $(DPREF base, LuaObject) through this reference. 13 LuaObject object; 14 15 alias object this; 16 17 package this(lua_State* L, int idx) 18 { 19 LuaObject.checkType(L, idx, LUA_TTABLE, "LuaTable"); 20 object = LuaObject(L, idx); 21 } 22 23 /** 24 * Lookup a value in this table or in a sub-table of this table. 25 * Params: 26 * T = type of value 27 * args = list of keys, where all keys but the last one should result in a table 28 * Returns: 29 * $(D t[k]) where $(D t) is the table for the second-to-last parameter, and $(D k) is the last parameter 30 * 31 * Examples: 32 * ---------------------- 33 auto execute = lua.get!LuaFunction("os", "execute"); 34 execute(`echo hello, world!`); 35 * ---------------------- 36 */ 37 T get(T, U...)(U args) @trusted 38 { 39 this.push(); 40 41 foreach(key; args) 42 { 43 pushValue(this.state, key); 44 lua_gettable(this.state, -2); 45 } 46 47 auto ret = getValue!T(this.state, -1); 48 lua_pop(this.state, args.length + 1); 49 return ret; 50 } 51 52 /** 53 * Read a string value in this table without making a copy of the string. 54 * The read string is passed to $(D dg), and should not be escaped. 55 * If the value for $(D key) is not a string, $(D dg) is not called. 56 * Params: 57 * key = lookup _key 58 * dg = delegate to receive string 59 * Returns: 60 * $(D true) if the value for $(D key) was a string and passed to $(D dg), $(D false) otherwise 61 * Examples: 62 -------------------- 63 t[2] = "two"; 64 t.readString(2, str => assert(str == "two")); 65 -------------------- 66 */ 67 bool readString(T)(T key, scope void delegate(in char[] str) dg) @trusted 68 { 69 this.push(); 70 scope(exit) lua_pop(this.state, 1); 71 72 pushValue(this.state, key); 73 74 lua_gettable(this.state, -2); 75 scope(exit) lua_pop(this.state, 1); 76 77 if(lua_isstring(this.state, -1) == 0) 78 return false; 79 80 size_t len; 81 const(char)* cstr = lua_tolstring(this.state, -1, &len); 82 83 dg(cstr[0 .. len]); 84 return true; 85 } 86 87 /** 88 * Same as calling $(D get!LuaObject) with the same arguments. 89 * Examples: 90 * --------------------- 91 auto luapath = lua["package", "path"]; 92 writefln("LUA_PATH:\n%s", luapath); 93 * --------------------- 94 * See_Also: 95 * $(MREF LuaTable.get) 96 */ 97 LuaObject opIndex(T...)(T args) 98 { 99 return get!LuaObject(args); 100 } 101 102 /** 103 * Set a key-value pair in this table. 104 * Params: 105 * key = key to _set 106 * value = value for $(D key) 107 */ 108 void set(T, U)(T key, U value) @trusted 109 { 110 this.push(); 111 scope(success) lua_pop(this.state, 1); 112 113 pushValue(this.state, key); 114 pushValue(this.state, value); 115 lua_settable(this.state, -3); 116 } 117 118 /** 119 * Set a key-value pair this table or in a sub-table of this table. 120 * Params: 121 * value = value to set 122 * args = list of keys, where all keys but the last one should result in a table 123 * Returns: 124 * $(D t[k] = value), where $(D t) is the table for the second-to-last parameter in args, 125 * and $(D k) is the last parameter in args 126 * 127 * Examples: 128 * ---------------------- 129 lua["string", "empty"] = (in char[] s){ return s.length == 0; }; 130 lua.doString(`assert(string.empty(""))`); 131 * ---------------------- 132 */ 133 void opIndexAssign(T, U...)(T value, U args) @trusted 134 { 135 this.push(); 136 scope(success) lua_pop(this.state, 1); 137 138 foreach(i, arg; args) 139 { 140 static if(i != args.length - 1) 141 { 142 pushValue(this.state, arg); 143 lua_gettable(this.state, -2); 144 } 145 } 146 147 pushValue(this.state, args[$-1]); 148 pushValue(this.state, value); 149 lua_settable(this.state, -3); 150 151 lua_pop(this.state, args.length - 1); 152 } 153 154 /** 155 * Create struct of type $(D T) and fill its members with fields from this table. 156 * 157 * Struct fields that are not present in this table are left at their default value. 158 * 159 * Params: 160 * T = any struct type 161 * 162 * Returns: 163 * Newly created struct 164 */ 165 T toStruct(T)() @trusted if (is(T == struct)) 166 { 167 push(); 168 return popValue!T(this.state); 169 } 170 171 /** 172 * Fill a struct's members with fields from this table. 173 * Params: 174 * s = struct to fill 175 */ 176 void copyTo(T)(ref T s) @trusted if (is(T == struct)) 177 { 178 push(); 179 fillStruct(this.state, -1, s); 180 lua_pop(L, 1); 181 } 182 183 /** 184 * Set the metatable for this table. 185 * Params: 186 * meta = new metatable 187 */ 188 void setMetaTable(ref LuaTable meta) @trusted 189 in{ assert(this.state == meta.state); } 190 body 191 { 192 this.push(); 193 meta.push(); 194 lua_setmetatable(this.state, -2); 195 lua_pop(this.state, 1); 196 } 197 198 /** 199 * Get the metatable for this table. 200 * Returns: 201 * A reference to the metatable for this table. The reference is nil if this table has no metatable. 202 */ 203 LuaTable getMetaTable() @trusted 204 { 205 this.push(); 206 scope(success) lua_pop(this.state, 1); 207 208 return lua_getmetatable(this.state, -1) == 0? LuaTable() : popValue!LuaTable(this.state); 209 } 210 211 /** 212 * Get the array length of the table. 213 */ 214 size_t length() @trusted 215 { 216 this.push(); 217 size_t len = lua_objlen(this.state, -1); 218 lua_pop(this.state, 1); 219 return len; 220 } 221 222 /** 223 * Iterate over the values in this table. 224 */ 225 int opApply(T)(int delegate(ref T value) dg) @trusted 226 { 227 this.push(); 228 lua_pushnil(this.state); 229 while(lua_next(this.state, -2) != 0) 230 { 231 auto value = popValue!T(this.state); 232 int result = dg(value); 233 if(result != 0) 234 { 235 lua_pop(this.state, 2); 236 return result; 237 } 238 } 239 lua_pop(this.state, 1); 240 return 0; 241 } 242 243 /** 244 * Iterate over the key-value pairs in this table. 245 */ 246 int opApply(T, U)(int delegate(ref U key, ref T value) dg) @trusted 247 { 248 this.push(); 249 lua_pushnil(this.state); 250 while(lua_next(this.state, -2) != 0) 251 { 252 auto value = popValue!T(this.state); 253 auto key = getValue!U(this.state, -1); 254 255 int result = dg(key, value); 256 if(result != 0) 257 { 258 lua_pop(this.state, 2); 259 return result; 260 } 261 } 262 lua_pop(this.state, 1); 263 return 0; 264 } 265 } 266 267 unittest 268 { 269 lua_State* L = luaL_newstate(); 270 scope(success) 271 { 272 assert(lua_gettop(L) == 0); 273 lua_close(L); 274 } 275 276 lua_newtable(L); 277 auto t = popValue!LuaTable(L); 278 279 assert(t.type == LuaType.Table); 280 281 t.set("foo", "bar"); 282 assert(t.get!string("foo") == "bar"); 283 284 t.set("foo", nil); 285 assert(t.get!LuaObject("foo").isNil); 286 287 t.set("foo", ["outer": ["inner": "hi!"]]); 288 auto s = t.get!(string)("foo", "outer", "inner"); 289 assert(s == "hi!"); 290 291 auto o = t["foo", "outer"]; 292 assert(o.type == LuaType.Table); 293 294 t["foo", "outer", "inner"] = "hello!"; 295 auto s2 = t.get!(string)("foo", "outer", "inner"); 296 assert(s2 == "hello!"); 297 298 // length 299 t.set("array", ["one", "two"]); 300 auto a = t.get!LuaTable("array"); 301 assert(a.length == 2); 302 303 // readString 304 t[2] = "two"; 305 bool success = t.readString(2, (in char[] str) { 306 assert(str == "two"); 307 }); 308 assert(success); 309 310 t[2] = true; 311 success = t.readString(2, (in char[] str) { assert(false); }); 312 assert(!success); 313 314 // metatable 315 pushValue(L, ["__index": (LuaObject self, string key){ 316 return key; 317 }]); 318 auto meta = popValue!LuaTable(L); 319 320 lua_newtable(L); 321 auto t2 = popValue!LuaTable(L); 322 323 t2.setMetaTable(meta); 324 325 auto test = t2.get!string("foobar"); 326 assert(test == "foobar"); 327 328 assert(t2.getMetaTable() == meta); 329 330 // opApply 331 auto input = [1, 2, 3]; 332 pushValue(L, input); 333 auto applyTest = popValue!LuaTable(L); 334 335 int i = 0; 336 foreach(int v; applyTest) 337 { 338 assert(input[i++] == v); 339 } 340 341 auto inputWithKeys = ["one": 1, "two": 2, "three": 3]; 342 pushValue(L, inputWithKeys); 343 auto applyTestKeys = popValue!LuaTable(L); 344 345 foreach(string key, int value; applyTestKeys) 346 { 347 assert(inputWithKeys[key] == value); 348 } 349 }