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 }