1 module handlebars.components.each;
2 
3 import handlebars.components.base;
4 import handlebars.tpl;
5 
6 import std.exception;
7 import std.conv;
8 import std.traits;
9 import std.algorithm;
10 
11 /// Component that will handle the if blocks
12 class EachComponentCt(Token[] tokens, Properties properties) : HbsComponent!"" {
13   enum ComponentName = "each";
14 
15   ///
16   string render(T, Components...)(T controller) {
17     string result;
18 
19     static if(properties.localName != "") {
20       mixin(`alias NameType = typeof(controller.` ~ properties.name ~ `);`);
21       static if(isCallable!(NameType)) {
22         alias __Type = ForeachType!(ReturnType!NameType);
23       } else {
24         alias __Type = ForeachType!(NameType);
25       }
26     }
27 
28     class TmpController {
29       private T controller;
30 
31       static if(properties.localName != "") {
32         mixin(`Unqual!(__Type) ` ~ properties.localName ~ `;`);
33       }
34 
35       static if(properties.indexName != "") {
36         mixin(`string ` ~ properties.indexName ~ `;`);
37       }
38 
39       mixin(genControllerFields!T());
40     }
41 
42     auto tmpController = new TmpController;
43     tmpController.controller = controller;
44 
45     mixin(genForeach());
46 
47     return result;
48   }
49 
50   private static string genForeach() {
51     string result;
52 
53     result ~= `foreach(i, value; controller.` ~ properties.name ~ `) {`;
54 
55     static if(properties.localName != "") {
56       result ~= `tmpController.` ~ properties.localName ~ ` = value;`;
57     }
58 
59     static if(properties.indexName != "") {
60       result ~= `tmpController.` ~ properties.indexName ~ ` = i.to!string;`;
61     }
62 
63     result ~= `result ~= handlebars.tpl.render!(tokens, TmpController, Components)(tmpController);`;
64 
65     result ~= `}`;
66 
67     return result;
68   }
69 
70   private static string genControllerFields(T)() {
71     static immutable ignoredMembers = [ __traits(allMembers, Object), "this", "ComponentName", "lifecycle",
72       "render", properties.localName, properties.indexName ];
73 
74     string result;
75 
76     static foreach (memberName; __traits(allMembers, T)) {{
77       static if(!ignoredMembers.canFind(memberName)) {
78         enum protection = __traits(getProtection, __traits(getMember, T, memberName));
79 
80         static if (protection == "public") {
81           mixin(`alias field = T.` ~ memberName ~ `;`);
82 
83           static if(isCallable!(field) && arity!field > 0) {
84             result ~= `auto ` ~ memberName ~ `(` ~ genParams!(Parameters!field) ~ `) {
85                         return controller.` ~ memberName ~ `(` ~  genVals!(Parameters!field) ~ `);
86                       }`;
87           } else {
88             result ~= `auto ` ~ memberName ~ `() { return controller.` ~ memberName ~ `; }`;
89           }
90         }
91       }
92     }}
93 
94     return result;
95   }
96 
97   private static string genParams(T...)() {
98     string result;
99 
100     return result;
101   }
102 
103   private static string genVals(T...)() {
104     string result;
105 
106     return result;
107   }
108 }
109 
110 /// Component that will handle the if blocks
111 class EachComponent : HbsComponent!"" {
112   enum ComponentName = "each";
113 
114   private {
115     string name;
116     string localName;
117     string indexName;
118   }
119 
120   ///
121   this(Properties properties) {
122     enforce!RenderComponentException(properties.list.length == 0, "list as |item| or list as |item index| expected");
123     enforce!RenderComponentException(properties.name != "", "list as |item| or list as |item index| expected");
124     enforce!RenderComponentException(properties.localName != "", "list as |item| or list as |item index| expected");
125 
126     this.name = properties.name;
127     this.localName = properties.localName;
128     this.indexName = properties.indexName;
129   }
130 
131   ///
132   string render(Component, Components...)() {
133     string result;
134 
135     long len = this.lifecycle.evaluateLong(this.name ~ ".length");
136 
137     foreach(i; 0..len) {
138       Token[] list;
139       string props = `"` ~ name ~ `" "` ~ localName ~ `" "` ~ i.to!string ~ `" `;
140 
141       if(indexName != "") {
142         props ~= `"` ~ indexName ~ `"`;
143       } else {
144         props ~= `""`;
145       }
146 
147       list ~= Token(Token.Type.openBlock, "scope", Properties(props));
148       list ~= this.content;
149       list ~= Token(Token.Type.closeBlock, "scope");
150 
151       result ~= lifecycle.yield(list);
152     }
153 
154     return result;
155   }
156 }