1 /** 2 * Wrapper for translating Jinja calls into native D function calls 3 * 4 * Copyright: 5 * Copyright (c) 2018, Maxim Tyapkin. 6 * Authors: 7 * Maxim Tyapkin 8 * License: 9 * This software is licensed under the terms of the BSD 3-clause license. 10 * The full terms of the license can be found in the LICENSE.md file. 11 */ 12 13 module djinja.algo.wrapper; 14 15 private 16 { 17 import std.algorithm : min; 18 import std.format : fmt = format; 19 import std.functional : toDelegate; 20 import std.traits; 21 import std.typecons : Tuple; 22 import std..string : join; 23 24 import djinja.exception : assertJinja = assertJinjaException; 25 import djinja.uninode; 26 } 27 28 29 alias Function = UniNode delegate(UniNode); 30 31 32 template wrapper(alias F) 33 if (isSomeFunction!F) 34 { 35 alias ParameterIdents = ParameterIdentifierTuple!F; 36 alias ParameterTypes = Parameters!F; 37 alias ParameterDefs = ParameterDefaults!F; 38 alias RT = ReturnType!F; 39 alias PT = Tuple!ParameterTypes; 40 41 42 Function wrapper() 43 { 44 UniNode func (UniNode params) 45 { 46 assertJinja(params.kind == UniNode.Kind.object, "Non object params"); 47 assertJinja(cast(bool)("varargs" in params), "Missing varargs in params"); 48 assertJinja(cast(bool)("kwargs" in params), "Missing kwargs in params"); 49 50 bool[string] filled; 51 PT args; 52 53 foreach(i, def; ParameterDefs) 54 { 55 alias key = ParameterIdents[i]; 56 static if (key == "varargs") 57 args[i] = UniNode.emptyArray; 58 else static if (key == "kwargs") 59 args[i] = UniNode.emptyObject; 60 else static if (!is(def == void)) 61 args[i] = def; 62 else 63 filled[key] = false; 64 } 65 66 void fillArg(size_t idx, PType)(string key, UniNode val) 67 { 68 // TODO toBoolType, toStringType 69 try 70 args[idx] = val.deserialize!PType; 71 catch 72 assertJinja(0, "Can't deserialize param `%s` from `%s` to `%s` in function `%s`" 73 .fmt(key, val.kind, PType.stringof, fullyQualifiedName!F)); 74 } 75 76 UniNode varargs = UniNode.emptyArray; 77 UniNode kwargs = UniNode.emptyObject; 78 79 bool isVarargs = false; 80 int varargsFilled = 0; 81 static foreach (int i; 0 .. PT.length) 82 { 83 static if (ParameterIdents[i] == "varargs" || ParameterIdents[i] == "kwargs") 84 { 85 isVarargs = true; 86 } 87 if (params["varargs"].length > i) 88 { 89 if (!isVarargs) 90 { 91 fillArg!(i, ParameterTypes[i])(ParameterIdents[i], params["varargs"][i]); 92 filled[ParameterIdents[i]] = true; 93 } 94 else 95 varargs ~= params["varargs"][i]; 96 varargsFilled++; 97 } 98 } 99 // Filled missed varargs 100 if (varargsFilled < params["varargs"].length) 101 foreach(i; varargsFilled .. params["varargs"].length) 102 varargs ~= params["varargs"][i]; 103 104 bool[string] kwargsFilled; 105 static foreach(i, key; ParameterIdents) 106 { 107 if (key in params["kwargs"]) 108 { 109 fillArg!(i, ParameterTypes[i])(key, params["kwargs"][key]); 110 filled[ParameterIdents[i]] = true; 111 kwargsFilled[key] = true; 112 } 113 } 114 // Filled missed kwargs 115 foreach (string key, ref UniNode val; params["kwargs"]) 116 { 117 if (key !in kwargsFilled) 118 kwargs[key] = val; 119 } 120 121 // Fill varargs/kwargs 122 foreach(i, key; ParameterIdents) 123 { 124 static if (key == "varargs") 125 args[i] = varargs; 126 else static if (key == "kwargs") 127 args[i] = kwargs; 128 } 129 130 string[] missedArgs = []; 131 foreach(key, val; filled) 132 if (!val) 133 missedArgs ~= key; 134 135 if (missedArgs.length) 136 assertJinja(0, "Missed values for args `%s`".fmt(missedArgs.join(", "))); 137 138 static if (is (RT == void)) 139 { 140 F(args.expand); 141 return UniNode(null); 142 } 143 else 144 { 145 auto ret = F(args.expand); 146 return ret.serialize!RT; 147 } 148 } 149 150 return toDelegate(&func); 151 } 152 }