1 module handlebars.components.all;
2 
3 public import handlebars.components.base;
4 public import handlebars.components.each;
5 public import handlebars.components.scope_;
6 public import handlebars.components.if_;
7 
8 
9 import handlebars.helper;
10 
11 import std.algorithm;
12 import std.traits;
13 import std.exception;
14 import std.conv;
15 
16 ///
17 struct ComponentGroup(Components...) {
18 
19   static:
20     ///
21     bool exists(string componentName) {
22       static foreach(Component; Components) {{
23 
24         static if (__traits(hasMember, Component, "ComponentName")) {
25           immutable name = Component.ComponentName;
26         } else {
27           immutable name = Component.stringof;
28         }
29 
30         if(componentName == name) {
31           return true;
32         }
33       }}
34 
35       return false;
36     }
37 
38     ///
39     string get(T)(T controller, Token token, Token[] content, Lifecycle lifecycle) {
40       auto instance = getInstance(controller, token);
41       instance.lifecycle = lifecycle;
42       instance.content = content;
43 
44       enforce!RenderComponentException(instance !is null,
45         "Can't initilize component `" ~ token.value ~ "`.");
46 
47       static foreach(Component; Components) {
48         if(Component.stringof == token.value) {
49           setupFields(controller, cast(Component) instance, token.properties);
50           return (cast(Component)instance).render!(Component, Components)();
51         }
52 
53         static if(__traits(hasMember, Component, "ComponentName")) {
54           if(Component.ComponentName == token.value) {
55             setupFields(controller, cast(Component) instance, token.properties);
56             return (cast(Component)instance).render!(Component, Components)();
57           }
58         }
59       }
60 
61       throw new RenderComponentException("Can't render component `" ~ token.value ~ "`");
62     }
63 
64     ///
65     string get(Token[] tokens, T)(T controller, Lifecycle lifecycle) {
66       enum token = tokens[0];
67 
68       static if(tokens.length >= 1) {
69         enum content = tokens[1..$-1];
70       } else {
71         enum content = Token.empty;
72       }
73 
74       auto instance = getInstance!(token, content)(controller);
75       instance.lifecycle = lifecycle;
76 
77       enforce!RenderComponentException(instance !is null,
78         "Can't initilize component `" ~ token.value ~ "`.");
79 
80       return instance.render!(T, Components)(controller);
81     }
82 
83     ///
84     IHbsComponent getInstance(T)(T controller, Token token) {
85       static foreach(Component; Components) {{
86         static if (__traits(hasMember, Component, "ComponentName")) {
87           immutable name = Component.ComponentName;
88         } else {
89           immutable name = Component.stringof;
90         }
91 
92         if(token.value == name) {
93           static if(__traits(hasMember, Component, "__ctor")) {
94             static foreach (t; __traits(getOverloads, Component, "__ctor")) {
95 
96               static if(arity!t != 1 || (arity!t == 1 && !is(Parameters!t[0] == Properties))) {
97                 if(arity!t == token.properties.list.length) {
98                   mixin(genHelperValues!("ctor", Parameters!t));
99                   mixin("return new " ~ Component.stringof ~ "(" ~ genHelperParams!("ctor", Parameters!t) ~ ");");
100                 }
101               }
102             }
103           } else {
104             return new Component();
105           }
106         }
107       }}
108 
109       static foreach(Component; Components) {{
110         static if (__traits(hasMember, Component, "ComponentName")) {
111           immutable name = Component.ComponentName;
112         } else {
113           immutable name = Component.stringof;
114         }
115 
116         if(token.value == name) {
117           static if(__traits(hasMember, Component, "__ctor")) {
118             static foreach (t; __traits(getOverloads, Component, "__ctor")) {
119               static if(arity!t == 1 && is(Parameters!t[0] == Properties)) {
120                 mixin("return new " ~ Component.stringof ~ "(token.properties);");
121               }
122             }
123           }
124         }
125       }}
126 
127       throw new RenderComponentException("The `"~token.value~"` component can't be rendered with the provided fields.");
128     }
129 
130     ///
131     auto getInstance(Token token, Token[] content, T)(T controller) {
132       static foreach(Component; Components) {
133         static if (__traits(hasMember, Component, "ComponentName") && token.value == Component.ComponentName) {
134           alias SelectedComponent = Component;
135         } else static if(token.value == Component.stringof) {
136           alias SelectedComponent = Component;
137         }
138       }
139 
140       return getInstance!(SelectedComponent, token.properties, content)(controller);
141     }
142 
143     ///
144     auto getInstance(ClassName, Properties properties, Token[] content, T)(T controller) {
145       static if(content.length > 0) {
146         enum init = "new " ~ ClassName.stringof ~ "Ct!(content, properties)";
147       } else {
148         enum init = "new " ~ ClassName.stringof;
149       }
150 
151       static if(properties.list.length > 0 && __traits(hasMember, ClassName, "__ctor")) {
152         alias t = ClassName.__ctor;
153 
154         mixin(genHelperPropertiesValues!("ctor", Parameters!t));
155         enum ctorParams = `(` ~ genHelperParams!("ctor", Parameters!t) ~ `)`;
156       } else {
157         enum ctorParams = "()";
158       }
159 
160       mixin("return " ~ init ~ ctorParams ~ ";");
161     }
162 
163     ///
164     private void setupFields(T, U)(T controller, U instance, Properties properties) {
165       static immutable ignoredMembers = [ __traits(allMembers, Object), "ComponentName", "lifecycle" ];
166 
167       static foreach (memberName; __traits(allMembers, U)) {{
168         enum protection = __traits(getProtection, __traits(getMember, U, memberName));
169 
170         static if(protection == "public" && memberName != "this" && memberName != "content" && !ignoredMembers.canFind(memberName)) {
171           mixin(`alias field = U.` ~ memberName ~ `;`);
172 
173           static if(!isCallable!field && !is(typeof(field) == void)) {
174             enum property = `properties.hash["` ~ memberName ~ `"]`;
175 
176             if(memberName in properties.hash) {
177               mixin(`if(!`~property~`.isEvaluated) {
178                 instance.` ~ memberName ~ ` = evaluate!(` ~ typeof(field).stringof ~ `)(controller, ` ~ property ~ `.value);
179               }`);
180 
181               static if(isSomeString!(typeof(field)) || isBuiltinType!(typeof(field))) {
182                 mixin(`if(`~property~`.isEvaluated) {
183                   instance.` ~ memberName ~ ` = `~property~`.value.to!(` ~ typeof(field).stringof ~ `);
184                 }`);
185               } else {
186                 mixin(`if(`~property~`.isEvaluated) {
187                   throw new RenderComponentException("Can't pass evaluated property to ` ~ memberName ~ `");
188                 }`);
189               }
190             }
191           }
192         }
193       }}
194     }
195 }