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 }