1 /**
2  * Temple (C) Dylan Knutson, 2013, 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.temple_context;
21 
22 import temple.temple;
23 import temple.output_stream;
24 
25 public import std.variant : Variant;
26 private import std.array, std.string, std.typetuple;
27 
28 final class TempleContext
29 {
30 private:
31 	// First hook is called to set the output buffer, the second is to unset it.
32 	alias TemplateHooks = TypeTuple!(void delegate(OutputStream), void delegate());
33 	TemplateHooks[0][] pushBuffHooks;
34 	TemplateHooks[1][] popBuffHooks;
35 
36 	Variant[string] vars;
37 
38 	TempleFuncType* yielded_template;
39 
40 
41 	TemplateHooks[0] getPushBuffHook()
42 	{
43 		return pushBuffHooks[$-1];
44 	}
45 
46 	TemplateHooks[1] getPopBuffHook()
47 	{
48 		return popBuffHooks[$-1];
49 	}
50 
51 package:
52 	/// package
53 	void __templePopHooks()
54 	{
55 		pushBuffHooks.length--;
56 		popBuffHooks.length--;
57 	}
58 
59 	/// package
60 	void __templePushHooks(TemplateHooks h)
61 	{
62 		this.pushBuffHooks ~= h[0];
63 		this.popBuffHooks ~= h[1];
64 	}
65 
66 	/// package
67 	static AppenderOutputStream __templeRenderWith(TempleFuncType* render_func, TempleContext ctx = null)
68 	in { assert(render_func !is null); }
69 	body
70 	{
71 		// Ensure a context is always present
72 		if(ctx is null)
73 		{
74 			ctx = new TempleContext();
75 		}
76 		// Allocate a buffer and call the render func with it
77 		auto buff = new AppenderOutputStream();
78 		render_func(buff, ctx);
79 
80 		return buff;
81 	}
82 
83 public:
84 	string capture(T...)(void delegate(T) block, T args)
85 	{
86 		auto buffer = new AppenderOutputStream();
87 		scope(exit) { buffer.clear(); }
88 
89 		// Make the template push to a new, blank buffer
90 		this.getPushBuffHook()(buffer);
91 
92 		// Call the block (which resides inside the template, and will
93 		// now write to `buffer`)
94 		block(args);
95 
96 		// Pop `buffer` out of the template
97 		this.getPopBuffHook()();
98 
99 		return buffer.data;
100 	}
101 
102 	bool isSet(string name)
103 	{
104 		return (name in vars && vars[name] != Variant());
105 	}
106 
107 	ref Variant var(string name)
108 	{
109 		if(name !in vars)
110 			vars[name] = Variant();
111 
112 		return vars[name];
113 	}
114 
115 	void opIndexAssign(T)(string name, T val) {
116 		if(name !in vars)
117 			vars[name] = Variant();
118 
119 		vars[name] = val;
120 	}
121 
122 	VarDispatcher var()
123 	{
124 		return VarDispatcher(this);
125 	}
126 
127 	Variant opDispatch(string op)() @property
128 	{
129 		return vars[op];
130 	}
131 
132 	void opDispatch(string op, T)(T other) @property
133 	{
134 		vars[op] = other;
135 	}
136 
137 	/**
138 	 * Methods/properties relating to layout support
139 	 */
140 	void partial(TempleFuncType* temple_func) @property
141 	{
142 		yielded_template = temple_func;
143 	}
144 
145 	auto partial() @property
146 	{
147 		return yielded_template;
148 	}
149 
150 	AppenderOutputStream yield() @property
151 	{
152 		auto buff = new AppenderOutputStream();
153 
154 		if(yielded_template !is null)
155 		{
156 			(*yielded_template)(buff, this);
157 		}
158 
159 		return buff;
160 	}
161 }
162 
163 private struct VarDispatcher
164 {
165 private:
166 	TempleContext context;
167 
168 public:
169 	this(TempleContext context)
170 	{
171 		this.context = context;
172 	}
173 
174 	ref Variant opDispatch(string op)() @property
175 	{
176 		return context.var(op);
177 	}
178 
179 	void opDispatch(string op, T)(T other) @property
180 	{
181 		context.var(op) = other;
182 	}
183 }
184 
185 unittest
186 {
187 	auto context = new TempleContext();
188 	context.foo = "bar";
189 	context.bar = 10;
190 
191 	with(context)
192 	{
193 		assert(var.foo == "bar");
194 		assert(var.bar == 10);
195 
196 		var.baz = true;
197 		assert(var.baz == true);
198 	}
199 }