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 }