1 /**
2 Internal module for pushing and getting variants (from $(STDMODULE _variant)).
3 Currently, only $(STDREF _variant,Algebraic) is supported.
4 */
5 module luad.conversions.variant;
6 
7 import luad.c.all;
8 
9 import luad.stack;
10 import luad.base;
11 import std.traits;
12 import std.variant;
13 import std.typetuple;
14 
15 void pushVariant(T)(lua_State* L, ref T value) if(isVariant!T)
16 {
17 	if(!value.hasValue())
18 	{
19 		lua_pushnil(L);
20 		return;
21 	}
22 
23 	foreach(Type; value.AllowedTypes)
24 	{
25 		if(auto v = value.peek!Type)
26 		{
27 			pushValue(L, *v);
28 			return;
29 		}
30 	}
31 
32 	assert(false);
33 }
34 
35 T getVariant(T)(lua_State* L, int idx) if (isVariant!T)
36 {
37 	auto t = lua_type(L, idx);
38 
39 	foreach(Type; T.AllowedTypes)
40 		if(t == luaTypeOf!Type)
41 			return T(getValue!Type(L, idx));
42 
43 	assert(false); // TODO: runtime error
44 }
45 
46 bool isAllowedType(T)(lua_State* L, int idx) {
47 	auto t = lua_type(L, idx);
48 
49 	foreach(Type; T.AllowedTypes)
50 		if(t == luaTypeOf!Type)
51 			return true;
52 
53 	return false;
54 }
55 
56 template isVariant(T)
57 {
58 	enum isVariant = is(typeof(isVariantImpl(T.init)));
59 	static if(isVariant)
60 		static assert(
61 			T.AllowedTypes.length > 0,
62 			"Variant is not supported - use an instance of VariantN with an explicit AllowedTypes list, such as Algebraic"
63 		);
64 }
65 
66 private void isVariantImpl(size_t max, AllowedTypes...)(const VariantN!(max, AllowedTypes) v){}
67 
68 version(unittest) import luad.testing;
69 
70 unittest
71 {
72 	lua_State* L = luaL_newstate();
73 	scope(success) lua_close(L);
74 	luaL_openlibs(L);
75 
76 	version(none)
77 	{
78 		Variant v = 123;
79 		pushValue(L, v);
80 		assert(popValue!int(L) == 123);
81 	}
82 
83 	alias Algebraic!(real, string, bool) BasicLuaType;
84 
85 	BasicLuaType v = "test";
86 	pushValue(L, v);
87 	assert(lua_isstring(L, -1));
88 	assert(getValue!string(L, -1) == "test");
89 	assert(popValue!BasicLuaType(L) == "test");
90 
91 	v = 2.3L;
92 	pushValue(L, v);
93 	assert(lua_isnumber(L, -1));
94 	lua_setglobal(L, "num");
95 
96 	unittest_lua(L, `
97 		assert(num == 2.3)
98 	`);
99 
100 	v = true;
101 	pushValue(L, v);
102 	assert(lua_isboolean(L, -1));
103 	assert(popValue!bool(L));
104 
105 	struct S
106 	{
107 		int i;
108 		double n;
109 		string s;
110 
111 		void f(){}
112 	}
113 	pushValue(L, Algebraic!(S, int)(S(1, 2.3, "hello")));
114 	assert(lua_istable(L, -1));
115 	lua_setglobal(L, "struct");
116 
117 	unittest_lua(L, `
118 		for key, expected in pairs{i = 1, n = 2.3, s = "hello"} do
119 			local value = struct[key]
120 			assert(
121 				value == expected,
122 				("bad table pair: '%s' = '%s' (expected '%s')"):format(key, value, expected)
123 			)
124 		end
125 	`);
126 }
127