1 module zua.interop.functions;
2 import zua.interop;
3 import zua.vm.engine;
4 import std.typecons;
5 import std.traits;
6 import std.conv;
7 
8 /** Thrown when a value cannot be converted from the Lua to D side */
9 final class ConversionException : Exception {
10 
11 	/** Create a new ConversionException*/
12 	this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextChain = null) @nogc @safe pure nothrow {
13 		super(msg, file, line, nextChain);
14 	}
15 
16 }
17 
18 /**
19 
20 Convert function parameters from a DConsumable[] to D-side arguments
21 
22 Parameters:
23 preferredName = The name to use in ConversionExceptions
24 
25 */
26 auto convertParameters(QualifiedFunc, string preferredName = "?", int level = 2)(DConsumable[] args) {
27 	alias Func = Unqual!QualifiedFunc;
28 	static assert(isSomeFunction!Func, "expected function, got " ~ Func.stringof);
29 
30 	static if (variadicFunctionStyle!Func == Variadic.no) {
31 		alias Params = Parameters!Func;
32 
33 		static foreach (i; 0..Params.length) {
34 			static assert(isConvertible!(Params[i]), "parameter #" ~ to!string(i + 1) ~ " (" ~ Params[i].stringof
35 				~ ") is not convertible from a Lua value");
36 		}
37 
38 		static if (level <= 1) {
39 			if (Params.length > args.length) {
40 				throw new ConversionException("not enough parameters");
41 			}
42 			else if (Params.length < args.length) {
43 				throw new ConversionException("too many parameters");
44 			}
45 		}
46 
47 		Tuple!Params dsideArgs = void;
48 		static foreach (i; 0..Params.length) {{
49 			alias ParamType = Params[i];
50 			auto dv = i >= args.length ? DConsumable(null) : args[i];
51 			Nullable!ParamType res = dv.convert!(ParamType, level == 0);
52 			if (res.isNull) {
53 				// getValueType!ParamType isn't gonna be null, because convert will never fail with a dynamic type
54 				throw new ConversionException("bad argument #" ~ to!string(i + 1)
55 					~ " to '" ~ preferredName ~ "' (" ~ getValueType!ParamType.get.valueTypeToString
56 					~ " expected, got " ~ dv.makeInternalValue.type.valueTypeToString ~ ")");
57 			}
58 			else {
59 				dsideArgs[i] = res.get;
60 			}
61 		}}
62 
63 		return dsideArgs;
64 	}
65 	else static if (variadicFunctionStyle!Func == Variadic.typesafe) {
66 		alias Params = Parameters!Func[0..$ - 1];
67 
68 		static foreach (i; 0..Params.length) {
69 			static assert(isConvertible!(Params[i]), "parameter #" ~ to!string(i + 1) ~ " is not convertible from a Lua value");
70 		}
71 
72 		alias VarargArray = Parameters!Func[$ - 1];
73 		static if (!is(VarargArray == VarargElement[], VarargElement)) {
74 			static assert(0, "vararg parameter should be an array");
75 			// this should never realistically run ^
76 		}
77 		static assert(isConvertible!VarargElement, "vararg parameter is not convertible from a Lua value");
78 
79 		static if (level <= 1) {
80 			if (Params.length > args.length) {
81 				throw new ConversionException("not enough parameters");
82 			}
83 		}
84 
85 		Tuple!(Parameters!Func) dsideArgs = void;
86 		static foreach (i; 0..Params.length) {{
87 			alias ParamType = Params[i];
88 			auto dv = i >= args.length ? DConsumable(null) : args[i];
89 			Nullable!ParamType res = dv.convert!(ParamType, level == 0);
90 			if (res.isNull) {
91 				// getValueType!ParamType isn't gonna be null, because convert will never fail with a dynamic type
92 				throw new ConversionException("bad argument #" ~ to!string(i + 1)
93 					~ " to '" ~ preferredName ~ "' (" ~ getValueType!ParamType.get.valueTypeToString
94 					~ " expected, got " ~ dv.makeInternalValue.type.valueTypeToString ~ ")");
95 			}
96 			else {
97 				dsideArgs[i] = res.get;
98 			}
99 		}}
100 
101 		VarargArray vararg;
102 		foreach (i; Params.length..args.length) {
103 			auto dv = args[i];
104 			Nullable!VarargElement res = dv.convert!(VarargElement, level == 0);
105 			if (res.isNull) {
106 				// getValueType!VarargElement isn't gonna be null, because convert will never fail with a dynamic type
107 				throw new ConversionException("bad argument #" ~ to!string(i + 1)
108 					~ " to '" ~ preferredName ~ "' (" ~ getValueType!VarargElement.get.valueTypeToString
109 					~ " expected, got " ~ dv.makeInternalValue.type.valueTypeToString ~ ")");
110 			}
111 			else {
112 				vararg ~= res.get;
113 			}
114 		}
115 
116 		dsideArgs[$ - 1] = vararg;
117 		return dsideArgs;
118 	}
119 	else {
120 		static assert(0, "this type of variadic function is not supported; use typesafe variadics");
121 	}
122 }
123 
124 /** Convert the D-side return value of a native function to a DConsumable[] */
125 DConsumable[] convertReturn(QualifiedFunc)(ReturnType!(Unqual!QualifiedFunc) value) {
126 	alias Func = Unqual!QualifiedFunc;
127 	static assert(isSomeFunction!Func, "expected function");
128 
129 	alias Return = ReturnType!Func;
130 
131 	static assert(!is(Return == void), "return type should not be void");
132 
133 	static if (!isConvertible!Return && is(Return == ReturnElement[], ReturnElement)) {
134 		static assert(isConvertible!ReturnElement, "return type is not convertible to a Lua value");
135 
136 		DConsumable[] res;
137 		res.reserve(value.length);
138 
139 		foreach (element; value) {
140 			res ~= DConsumable(element);
141 		}
142 
143 		return res;
144 	}
145 	else {
146 		static assert(isConvertible!Return, "return type is not convertible to a Lua value");
147 
148 		return [DConsumable(value)];
149 	}
150 }