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, uint line = __LINE__, string file = __FILE__)()
41 {
42     import std.conv : to;
43     return compile_temple!(__TempleString, file~":"~line.to!string ~ ": InlineTemplate", __Filter);
44 }
45 deprecated("Please use compile_temple")
46 auto Temple(ARGS...)() {
47     return .compile_temple!(ARGS)();
48 }
49 
50 private
51 CompiledTemple compile_temple(
52 	string __TempleString,
53 	string __TempleName,
54 	__Filter = void)()
55 {
56 	// __TempleString: The template string to compile
57 	// __TempleName: The template's file name, or 'InlineTemplate'
58 	// __Filter: FP for the rendered template
59 
60 	// Is a Filter present?
61 	enum __TempleHasFP = !is(__Filter == void);
62 
63 	// Needs to be kept in sync with the param name of the Filter
64 	// passed to Temple
65 	enum __TempleFilterIdent = __TempleHasFP ? "__Filter" : "";
66 
67 	// Generates the actual function string, with the function name being
68 	// `TempleFunc`.
69 	const __TempleFuncStr = __temple_gen_temple_func_string(
70 		__TempleString,
71 		__TempleName,
72 		__TempleFilterIdent);
73 
74 	//pragma(msg, __TempleFuncStr);
75 
76 	#line 1 "TempleFunc"
77 	mixin(__TempleFuncStr);
78 	#line 75 "src/temple/temple.d"
79 
80 	static if(__TempleHasFP) {
81 		alias temple_func = TempleFunc!__Filter;
82 	}
83 	else {
84 		alias temple_func = TempleFunc;
85 	}
86 
87 	return CompiledTemple(&temple_func, null);
88 }
89 
90 /**
91  * TempleFile
92  * Compiles a file on the disk into a Temple render function
93  * Takes an optional Filter
94  */
95 CompiledTemple compile_temple_file(string template_file, Filter = void)()
96 {
97 	pragma(msg, "Compiling ", template_file, "...");
98 	return compile_temple!(import(template_file), template_file, Filter);
99 }
100 
101 deprecated("Please use compile_temple_file")
102 auto TempleFile(ARGS...)() {
103     return .compile_temple_file!(ARGS)();
104 }
105 
106 /**
107  * TempleFilter
108  * Curries a Temple to always use a given template filter, for convienence
109  */
110 template TempleFilter(Filter) {
111     template compile_temple(ARGS...) {
112         alias compile_temple = .compile_temple!(ARGS, Filter);
113     }
114     template compile_temple_file(ARGS...) {
115         alias compile_temple_file = .compile_temple_file!(ARGS, Filter);
116     }
117 
118     deprecated("Please compile_temple")      alias Temple     = compile_temple;
119     deprecated("Please compile_temple_file") alias TempleFile = compile_temple_file;
120 }
121 
122 /**
123  * CompiledTemple
124  */
125 package
126 struct CompiledTemple {
127 
128 package:
129     alias TempleFuncSig = void function(TempleContext);
130     TempleFuncSig render_func = null;
131 
132     // renderer used to handle 'yield's
133     const(CompiledTemple)* partial_rendr = null;
134 
135     this(TempleFuncSig rf, const(CompiledTemple*) cf)
136     in { assert(rf); }
137     body {
138         this.render_func = rf;
139         this.partial_rendr = cf;
140     }
141 
142 public:
143     //deprecated static Temple opCall()
144 
145     // render template directly to a string
146     string toString(TempleContext tc = null) const {
147         auto a = appender!string();
148         this.render(a, tc);
149         return a.data;
150     }
151 
152     // render using an arbitrary output range
153     void render(T)(ref T os, TempleContext tc = null) const
154     if(	isOutputRange!(T, string) &&
155     	!is(T == TempleOutputStream))
156     {
157     	auto oc = TempleOutputStream(os);
158     	return render(oc, tc);
159     }
160 
161     // render using a sink function (DMD can't seem to cast a function to a delegate)
162     void render(void delegate(string) sink, TempleContext tc = null) const {
163     	auto oc = TempleOutputStream(sink);
164     	this.render(oc, tc); }
165     void render(void function(string) sink, TempleContext tc = null) const {
166     	auto oc = TempleOutputStream(sink);
167     	this.render(oc, tc); }
168     void render(ref std.stdio.File f, TempleContext tc = null) const {
169         auto oc = TempleOutputStream(f);
170         this.render(oc, tc);
171     }
172 
173     // normalized render function, using an TempleOutputStream
174     package
175     void render(ref TempleOutputStream os, TempleContext tc) const
176     {
177         // the context never escapes the scope of a template, so it's safe
178         // to allocate a new context here
179         // TODO: verify this is safe
180         auto local_tc = scoped!TempleContext();
181 
182         // and always ensure that a template is passed, at the very least,
183         // an empty context (needed for various template scope book keeping)
184         if(tc is null) {
185             tc = local_tc.Scoped_payload;
186             //tc = new TempleContext();
187         }
188 
189         // template renders into given output stream
190         auto old = tc.sink;
191         scope(exit) tc.sink = old;
192         tc.sink = os;
193 
194         // use the layout if we've got one
195         if(this.partial_rendr !is null) {
196             auto old_partial = tc.partial;
197 
198                         tc.partial = this.partial_rendr;
199             scope(exit) tc.partial = old_partial;
200 
201             this.render_func(tc);
202         }
203         // else, call this render function directly
204         else {
205             this.render_func(tc);
206         }
207     }
208 
209     // render using a vibe.d OutputStream
210     version(Have_vibe_d) {
211         private import vibe.core.stream : OutputStream;
212         private import vibe.stream.wrapper : StreamOutputRange;
213 
214 		void render(vibe.core.stream.OutputStream os, TempleContext tc = null) {
215             static assert(isOutputRange!(vibe.stream.wrapper.StreamOutputRange, string));
216 
217             auto sor = StreamOutputRange(os);
218             this.render(sor, tc);
219         }
220     }
221 
222     CompiledTemple layout(const(CompiledTemple*) partial) const
223     in {
224         assert(this.partial_rendr is null, "attempting to set already-set partial of a layout");
225     }
226     body {
227     	return CompiledTemple(this.render_func, partial);
228     }
229 
230     invariant() {
231         assert(render_func);
232     }
233 }