1 /**
2  * Temple (C) Dylan Knutson, 2014, distributed under the:
3  * Boost Software License - Version 1.0 - August 17th, 2003
4  *
5  * Permission is hereby granted, free of charge, to any person or organization
6  * obtaining a copy of the software and accompanying documentation covered by
7  * this license (the "Software") to use, reproduce, display, distribute,
8  * execute, and transmit the Software, and to prepare derivative works of the
9  * Software, and to permit third-parties to whom the Software is furnished to
10  * do so, all subject to the following:
11  *
12  * The copyright notices in the Software and this entire statement, including
13  * the above license grant, this restriction and the following disclaimer,
14  * must be included in all copies of the Software, in whole or in part, and
15  * all derivative works of the Software, unless such copies or derivative
16  * works are solely in the form of machine-executable object code generated by
17  * a source language processor.
18  */
19 
20 module temple;
21 
22 private import
23 	temple.util,
24 	temple.delims,
25 	temple.func_string_gen;
26 private import std.array : appender, Appender;
27 private import std.range : isOutputRange;
28 private import std.typecons : scoped;
29 
30 public {
31     import temple.temple_context : TempleContext;
32 	import temple.output_stream  : TempleOutputStream, TempleInputStream;
33 	import temple.vibe;
34 }
35 
36 /**
37  * Temple
38  * Main template for generating Temple functions
39  */
40 CompiledTemple compile_temple(string __TempleString, __Filter = void)()
41 {
42     return compile_temple!(__TempleString, "InlineTemplate", __Filter);
43 }
44 deprecated("Please compile_temple")
45 auto Temple(ARGS...)() {
46     return .compile_temple!(ARGS)();
47 }
48 
49 private
50 CompiledTemple compile_temple(
51 	string __TempleString,
52 	string __TempleName,
53 	__Filter = void)()
54 {
55 	// __TempleString: The template string to compile
56 	// __TempleName: The template's file name, or 'InlineTemplate'
57 	// __Filter: FP for the rendered template
58 
59 	// Is a Filter present?
60 	enum __TempleHasFP = !is(__Filter == void);
61 
62 	// Needs to be kept in sync with the param name of the Filter
63 	// passed to Temple
64 	enum __TempleFilterIdent = __TempleHasFP ? "__Filter" : "";
65 
66 	// Generates the actual function string, with the function name being
67 	// `TempleFunc`.
68 	const __TempleFuncStr = __temple_gen_temple_func_string(
69 		__TempleString,
70 		__TempleName,
71 		__TempleFilterIdent);
72 
73 	//pragma(msg, __TempleFuncStr);
74 
75 	#line 1 "TempleFunc"
76 	mixin(__TempleFuncStr);
77 	#line 75 "src/temple/temple.d"
78 
79 	static if(__TempleHasFP) {
80 		alias temple_func = TempleFunc!__Filter;
81 	}
82 	else {
83 		alias temple_func = TempleFunc;
84 	}
85 
86 	return CompiledTemple(&temple_func, null);
87 }
88 
89 /**
90  * TempleFile
91  * Compiles a file on the disk into a Temple render function
92  * Takes an optional Filter
93  */
94 CompiledTemple compile_temple_file(string template_file, Filter = void)()
95 {
96 	pragma(msg, "Compiling ", template_file, "...");
97 	return compile_temple!(import(template_file), template_file, Filter);
98 }
99 
100 deprecated("Please compile_temple_file")
101 auto TempleFile(ARGS...)() {
102     return .compile_temple_file!(ARGS)();
103 }
104 
105 /**
106  * TempleFilter
107  * Curries a Temple to always use a given template filter, for convienence
108  */
109 template TempleFilter(Filter) {
110     template compile_temple(ARGS...) {
111         alias compile_temple = .compile_temple!(ARGS, Filter);
112     }
113     template compile_temple_file(ARGS...) {
114         alias compile_temple_file = .compile_temple_file!(ARGS, Filter);
115     }
116 
117     deprecated("Please compile_temple")      alias Temple     = compile_temple;
118     deprecated("Please compile_temple_file") alias TempleFile = compile_temple_file;
119 }
120 
121 /**
122  * CompiledTemple
123  */
124 package
125 struct CompiledTemple {
126 
127 package:
128     alias TempleFuncSig = void function(TempleContext);
129     TempleFuncSig render_func = null;
130 
131     // renderer used to handle 'yield's
132     const(CompiledTemple)* partial_rendr = null;
133 
134     this(TempleFuncSig rf, const(CompiledTemple*) cf)
135     in { assert(rf); }
136     body {
137         this.render_func = rf;
138         this.partial_rendr = cf;
139     }
140 
141 public:
142     //deprecated static Temple opCall()
143 
144     // render template directly to a string
145     string toString(TempleContext tc = null) const {
146         auto a = appender!string();
147         this.render(a, tc);
148         return a.data;
149     }
150 
151     // render using an arbitrary output range
152     void render(T)(ref T os, TempleContext tc = null) const
153     if(	isOutputRange!(T, string) &&
154     	!is(T == TempleOutputStream))
155     {
156     	auto oc = TempleOutputStream(os);
157     	return render(oc, tc);
158     }
159 
160     // render using a sink function (DMD can't seem to cast a function to a delegate)
161     void render(void delegate(string) sink, TempleContext tc = null) const {
162     	auto oc = TempleOutputStream(sink);
163     	this.render(oc, tc); }
164     void render(void function(string) sink, TempleContext tc = null) const {
165     	auto oc = TempleOutputStream(sink);
166     	this.render(oc, tc); }
167     void render(ref std.stdio.File f, TempleContext tc = null) const {
168         auto oc = TempleOutputStream(f);
169         this.render(oc, tc);
170     }
171 
172     // normalized render function, using an TempleOutputStream
173     package
174     void render(ref TempleOutputStream os, TempleContext tc) const
175     {
176         // the context never escapes the scope of a template, so it's safe
177         // to allocate a new context here
178         // TODO: verify this is safe
179         auto local_tc = scoped!TempleContext();
180 
181         // and always ensure that a template is passed, at the very least,
182         // an empty context (needed for various template scope book keeping)
183         if(tc is null) {
184             tc = local_tc.Scoped_payload;
185             //tc = new TempleContext();
186         }
187 
188         // template renders into given output stream
189         auto old = tc.sink;
190         scope(exit) tc.sink = old;
191         tc.sink = os;
192 
193         // use the layout if we've got one
194         if(this.partial_rendr !is null) {
195             auto old_partial = tc.partial;
196 
197                         tc.partial = this.partial_rendr;
198             scope(exit) tc.partial = old_partial;
199 
200             this.render_func(tc);
201         }
202         // else, call this render function directly
203         else {
204             this.render_func(tc);
205         }
206     }
207 
208     // render using a vibe.d OutputStream
209     version(Have_vibe_d) {
210         private import vibe.core.stream : OutputStream;
211         private import vibe.stream.wrapper : StreamOutputRange;
212 
213         void render(vibe.core.stream.OutputStream os) {
214             static assert(isOutputRange!(vibe.core.stream.OutputStream, string));
215 
216             auto sor = StreamOutputRange(os);
217             this.render(sor);
218         }
219     }
220 
221     CompiledTemple layout(const(CompiledTemple*) partial) const
222     in {
223         assert(this.partial_rendr is null, "attempting to set already-set partial of a layout");
224     }
225     body {
226     	return CompiledTemple(this.render_func, partial);
227     }
228 
229     invariant() {
230         assert(render_func);
231     }
232 }