1 module vayne.lib;
2 
3 
4 import std.algorithm;
5 import std.array;
6 import std.conv;
7 import std.format;
8 import std.string;
9 import std.utf;
10 import std.regex;
11 
12 
13 import vayne.value;
14 
15 
16 template bindVars(size_t i, alias Container, Vars...) {
17 	static if(i < Vars.length) {
18 		enum bindVars = Vars.length ? (__traits(identifier, Container) ~ "[\"" ~ __traits(identifier, Vars[i]) ~ "\"]=Value(Vars[" ~ i.to!string ~ "]);\n" ~ bindVars!(i + 1, Container, Vars)) : "";
19 	} else {
20 		enum bindVars = "";
21 	}
22 }
23 
24 
25 void bindLibBasic(ref Value[string] globals) {
26 	static long length(Value x) {
27 		return cast(long)x.length;
28 	}
29 
30 	static bool existsIn(Value[] args) {
31 		auto value = args[0].get!string;
32 		foreach (f; args[1..$])
33 			if(value == f.get!string)
34 				return true;
35 		return false;
36 	}
37 
38 	static bool empty(Value x) {
39 		return x.length == 0;
40 	}
41 
42 	static Value keys(Value x) {
43 		return x.keys();
44 	}
45 
46 	static Value values(Value x) {
47 		return x.values();
48 	}
49 
50 	static Value front(Value x) {
51 		return x[0];
52 	}
53 
54 	static Value back(Value x) {
55 		return x[x.length - 1];
56 	}
57 
58 	static Value take(Value x, Value y) {
59 		return x[0..min(x.length, y.get!long)];
60 	}
61 
62 	static Value get(Value x, Value key, Value def) {
63 		Value result;
64 		if (x.has(key, &result))
65 			return result;
66 		return def;
67 	}
68 
69 	static Value def(Value x, Value def) {
70 		if ((x.type == Value.Type.Undefined) || (x.type == Value.Type.Null))
71 			return def;
72 		return x;
73 	}
74 
75 	static long tointeger(Value x) {
76 		return x.get!long;
77 	}
78 
79 	static double tofloat(Value x) {
80 		return x.get!double;
81 	}
82 
83 	static string tostring(Value x) {
84 		return x.toString();
85 	}
86 
87 	static bool tobool(Value x) {
88 		return x.get!bool;
89 	}
90 
91 	static string type(Value x) {
92 		final switch (x.type) with (Value.Type) {
93 		case Undefined:
94 			return "undefined";
95 		case Null:
96 			return "null";
97 		case Bool:
98 			return "bool";
99 		case Integer:
100 			return "integer";
101 		case Float:
102 			return "float";
103 		case Function:
104 			return "function";
105 		case String:
106 			return "string";
107 		case Array:
108 			return "array";
109 		case AssocArray:
110 			return "assocarray";
111 		case Object:
112 			return "object";
113 		case Pointer:
114 			return "pointer";
115 		}
116 	}
117 
118 	static bool exists(Value x) {
119 		// Json objects also have a type property that is not a string, so in the case our Value is also a Json we have to convert it to string first.
120 		return x.type.to!string != "undefined";
121 	}
122 
123 	static string escape(Value[] args) {
124 		auto value = args[0].get!string;
125 
126 		foreach (f; args[1..$]) {
127 			switch (f.get!string) {
128 			case "html":
129 				value = escapeHTML(value);
130 				break;
131 			case "js":
132 				value = escapeJS(value);
133 				break;
134 			case "uri":
135 				value = escapeURI(value);
136 				break;
137 			default:
138 				assert("unimplemented escape filter: " ~ f.get!string);
139 				break;
140 			}
141 		}
142 
143 		return value;
144 	}
145 
146 	static string translate(Value[] args) {
147 		return args[0].get!string;
148 	}
149 
150 	static string replace(string x, string from, string to) {
151 		return x.replace(from, to);
152 	}
153 
154 	static string replaceAll(string x, string from, string to) {
155 		return x.replaceAll(regex(from), to);
156 	}
157 
158 	globals["length"] = Value(&length);
159 	globals["empty"] = Value(&empty);
160 	globals["keys"] = Value(&keys);
161 	globals["values"] = Value(&values);
162 	globals["front"] = Value(&front);
163 	globals["back"] = Value(&back);
164 	globals["take"] = Value(&take);
165 
166 	globals["integer"] = Value(&tointeger);
167 	globals["float"] = Value(&tofloat);
168 	globals["string"] = Value(&tostring);
169 	globals["bool"] = Value(&tobool);
170 	globals["type"] = Value(&type);
171 
172 	globals["get"] = Value(&get);
173 	globals["has"] = Value((Value x, Value key) => x.has(key));
174 	globals["default"] = Value(&def);
175 
176 	globals["escape"] = Value(&escape);
177 
178 	globals["replace"] = Value(&replace);
179 	globals["replaceAll"] = Value(&replaceAll);
180 	globals["existsIn"] = Value(&existsIn);
181 
182 	globals["__escape"] = Value(&escape);
183 	globals["__translate"] = Value(&translate);
184 }
185 
186 
187 void bindLibMath(ref Value[string] globals) {
188 	import std.math;
189 
190 	static Value abs(Value x) {
191 		final switch (x.type) with (Value.Type) {
192 		case Undefined:
193 		case Function:
194 		case String:
195 		case Array:
196 		case AssocArray:
197 		case Object:
198 		case Pointer:
199 		case Null:
200 		case Bool:
201 			return x;
202 		case Integer:
203 			return Value(std.math.abs(x.get!long));
204 		case Float:
205 			return Value(std.math.abs(x.get!double));
206 		}
207 	}
208 
209 	static Value min(Value[] args) {
210 		size_t argMin;
211 
212 		foreach (i; 1..args.length) {
213 			if (args[argMin].compareOp!">"(args[i]))
214 				argMin = i;
215 		}
216 
217 		return args[argMin];
218 	}
219 
220 	static Value max(Value[] args) {
221 		size_t argMax;
222 
223 		foreach (i; 1..args.length) {
224 			if (args[argMax].compareOp!"<"(args[i]))
225 				argMax = i;
226 		}
227 
228 		return args[argMax];
229 	}
230 
231 	globals["abs"] = Value(&abs);
232 	globals["min"] = Value(&min);
233 	globals["max"] = Value(&max);
234 }
235 
236 
237 void bindLibString(ref Value[string] globals) {
238 	static string join(Value[] x) {
239 		auto app = appender!string;
240 		auto value = x[0];
241 		auto len = value.length;
242 		auto seps = (x.length > 1) ? x[1].get!string : ",";
243 		foreach (size_t i, v; value) {
244 			app.put(v.get!string);
245 			if (i + 1 != len)
246 				app.put(seps);
247 		}
248 		return app.data;
249 	}
250 
251 	static string[] split(Value[] x) {
252 		auto value = x[0].get!string;
253 		auto sep = (x.length > 1) ? x[1].get!string : " ";
254 
255 		return value.split(sep);
256 	}
257 
258 	static string strip(Value x) {
259 		return x.get!string.strip;
260 	}
261 
262 	static string lower(Value x) {
263 		return x.get!string.toLower();
264 	}
265 
266 	static string upper(Value x) {
267 		return x.get!string.toUpper();
268 	}
269 
270 	static string capitalize(Value x) {
271 		return x.get!string.capitalize();
272 	}
273 
274 	static long indexOf(Value[] args) {
275 		if (!args[0].length)
276 			return -1;
277 
278 		if (args[0].type == Value.Type.String) {
279 			auto haystack = args[0].get!string;
280 			auto needle = args[1].get!string;
281 			auto start = (args.length > 2) ? args[2].get!size_t : 0;
282 			return haystack.indexOf(needle, start);
283 		}
284 
285 		foreach (size_t i, v; args[0]) {
286 			if (v.compareOp!"=="(args[1]))
287 				return cast(long)i;
288 		}
289 
290 		return -1;
291 	}
292 
293 	static string format(Value[] args) {
294 		auto fmt = args[0].get!string;
295 		auto spec = FormatSpec!char(fmt);
296 
297 		auto app = appender!string;
298 		app.reserve(max(32, fmt.length + fmt.length >> 1));
299 
300 		size_t arg = 1;
301     	while (spec.writeUpToNextSpec(app)) {
302 			if (arg >= args.length)
303 				break;
304 
305 			auto value = args[arg++];
306 			final switch (value.type) with (Value.Type) {
307 			case Undefined:
308 			case Function:
309 			case String:
310 			case Array:
311 			case AssocArray:
312 			case Object:
313 			case Pointer:
314 				formatValue(&app, value.get!string, spec);
315 				break;
316 			case Null:
317 				formatValue(&app, null, spec);
318 				break;
319 			case Bool:
320 				formatValue(&app, value.get!bool, spec);
321 				break;
322 			case Integer:
323 				formatValue(&app, value.get!long, spec);
324 				break;
325 			case Float:
326 				formatValue(&app, value.get!double, spec);
327 				break;
328 			}
329 		}
330 
331 		if (arg != args.length)
332 			throw new Exception(std.format.format("number of arguments doesn't match number of format specifiers - expected %d, got %d", arg - 1, args.length - 1));
333 		return app.data;
334 	}
335 
336 	globals["join"] = Value(&join);
337 	globals["split"] = Value(&split);
338 	globals["format"] = Value(&format);
339 	globals["strip"] = Value(&strip);
340 	globals["lower"] = Value(&lower);
341 	globals["upper"] = Value(&upper);
342 	globals["capitalize"] = Value(&capitalize);
343 	globals["indexOf"] = Value(&indexOf);
344 }
345 
346 
347 void bindLibDefault(ref Value[string] globals) {
348 	bindLibBasic(globals);
349 	bindLibString(globals);
350 	bindLibMath(globals);
351 }
352 
353 
354 string escapeHTML(string x) {
355 	auto app = appender!string;
356 	app.reserve(8 + x.length + (x.length >> 1));
357 
358 	foreach (dchar ch; x) {
359 		switch (ch) {
360 		case '"':
361 			app.put("&#34;");
362 			break;
363 		case '\'':
364 			app.put("&#39;");
365 			break;
366 		case '<':
367 			app.put("&lt;");
368 			break;
369 		case '>':
370 			app.put("&gt;");
371 			break;
372 		case '&':
373 			app.put("&amp;");
374 			break;
375 		default:
376 			app.put(ch);
377 			break;
378 		}
379 	}
380 	return app.data;
381 }
382 
383 
384 string escapeJS(string x) {
385 	auto app = appender!string;
386 	app.reserve(x.length + (x.length >> 1));
387 
388 	foreach (dchar ch; x) {
389 		switch (ch) {
390 		case '\\':
391 			app.put(`\\`);
392 			break;
393 		case '\'':
394 			app.put(`\'`);
395 			break;
396 		case '\"':
397 			app.put(`\"`);
398 			break;
399 		case '\r':
400 			break;
401 		case '\n':
402 			app.put(`\n`);
403 			break;
404 		default:
405 			app.put(ch);
406 			break;
407 		}
408 	}
409 	return app.data;
410 }
411 
412 
413 string escapeURI(string x) {
414 	auto app = appender!string;
415 	app.reserve(8 + x.length + (x.length >> 1));
416 
417 	foreach (i; 0..x.length) {
418 		switch (x.ptr[i]) {
419 		case 'A': .. case 'Z':
420 		case 'a': .. case 'z':
421 		case '0': .. case '9':
422 		case '-': case '_': case '.': case '~':
423 			app.put(x.ptr[i]);
424 			break;
425 		default:
426 			formattedWrite(&app, "%%%02X", x.ptr[i]);
427 			break;
428 		}
429 	}
430 
431 	return app.data;
432 }