1 module handlebars.helper; 2 3 import std.conv; 4 import std.string; 5 import std.algorithm; 6 import std.traits; 7 8 import handlebars.tokens; 9 import handlebars.properties; 10 11 /// 12 class RenderHelperException : Exception { 13 pure nothrow @nogc @safe this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) { 14 super(msg, file, line, nextInChain); 15 } 16 } 17 18 /// 19 string getHelper(U)(U value, Token token) if (is(U == struct) || is(U == class)) { 20 return getHelper(value, value, token); 21 } 22 23 /// 24 string getHelper(T, U)(T controller, U value, Token token, size_t pathStart = 0) if (is(U == struct) || is(U == class)) { 25 static immutable ignoredMembers = [ __traits(allMembers, Object), "content", "lifecycle", "render"]; 26 auto pieces = token.value.split('.')[pathStart..$]; 27 28 static foreach (memberName; __traits(allMembers, U)) {{ 29 static if(__traits(hasMember, U, memberName)) { 30 enum protection = __traits(getProtection, __traits(getMember, U, memberName)); 31 } else { 32 enum protection = ""; 33 } 34 35 static if(protection == "public" && memberName != "this" && !ignoredMembers.canFind(memberName)) { 36 if(pieces[0] == memberName) { 37 mixin(`alias field = U.` ~ memberName ~ `;`); 38 39 static if(isCallable!(field) && arity!field > 0) { 40 mixin(genHelperValues!(memberName, Parameters!field)); 41 mixin("return value." ~ memberName ~ "(" ~ genHelperParams!(memberName, Parameters!field) ~ ").to!string;"); 42 } else { 43 mixin("return getHelper(controller, value." ~ memberName ~ `, token, pathStart + 1);`); 44 } 45 } 46 } 47 }} 48 49 throw new RenderHelperException("`" ~ token.value ~ "` can not be rendered becaues it is not defined."); 50 } 51 52 /// 53 string getHelper(T, U)(T controller, U value, Token, size_t) if (!is(U == struct) && !is(U == class)) { 54 throw new RenderHelperException("The helpers must be inside a struct or a class."); 55 } 56 57 /// 58 static string genHelperValues(string prefix, List...)() { 59 string result; 60 size_t index; 61 62 static foreach (T; List) {{ 63 string property = `token.properties.list[` ~ index.to!string ~ `]`; 64 string var = prefix ~ "_" ~ index.to!string; 65 66 result ~= T.stringof ~ " " ~ var ~ ";"; 67 result ~= `if(` ~ property ~ `.isEvaluated) {`; 68 result ~= var ~ ` = `~ property ~`.get!(` ~ T.stringof ~ ");"; 69 result ~= `} else { ` ~ var ~ ` = evaluate!(` ~ T.stringof ~ `)(controller, ` ~ property ~ `.value); }`; 70 index++; 71 }} 72 73 return result; 74 } 75 76 /// 77 static string genHelperPropertiesValues(string prefix, List...)() { 78 string result; 79 size_t index; 80 81 static foreach (T; List) {{ 82 string property = `properties.list[` ~ index.to!string ~ `]`; 83 string var = prefix ~ "_" ~ index.to!string; 84 85 result ~= T.stringof ~ " " ~ var ~ ";"; 86 result ~= `if(` ~ property ~ `.isEvaluated) {`; 87 result ~= var ~ ` = `~ property ~`.get!(` ~ T.stringof ~ ");"; 88 result ~= `} else { ` ~ var ~ ` = evaluate!(` ~ T.stringof ~ `)(controller, ` ~ property ~ `.value); }`; 89 index++; 90 }} 91 92 return result; 93 } 94 95 /// 96 static string genHelperParams(string prefix, List...)() { 97 string result; 98 string glue; 99 size_t index; 100 101 static foreach (T; List) { 102 result ~= glue ~ prefix ~ "_" ~ index.to!string; 103 glue = ","; 104 index++; 105 } 106 107 return result; 108 } 109 110 /// 111 string helperParams(List...)(Properties.Property[] list) { 112 string result; 113 string glue; 114 115 size_t index; 116 117 static foreach (Type; List) {{ 118 result ~= glue ~ list[index].toParam ~ ".to!(" ~ Type.stringof ~ ")"; 119 glue = ", "; 120 index++; 121 }} 122 123 return result; 124 }