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 }