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 11 12 import vayne.value; 13 14 15 template bindVars(size_t i, alias Container, Vars...) { 16 static if(i < Vars.length) { 17 enum bindVars = Vars.length ? (__traits(identifier, Container) ~ "[\"" ~ __traits(identifier, Vars[i]) ~ "\"]=Value(Vars[" ~ i.to!string ~ "]);\n" ~ bindVars!(i + 1, Container, Vars)) : ""; 18 } else { 19 enum bindVars = ""; 20 } 21 } 22 23 24 void bindLibBasic(ref Value[string] globals) { 25 static long length(Value x) { 26 return cast(long)x.length; 27 } 28 29 static bool empty(Value x) { 30 return x.length == 0; 31 } 32 33 static Value keys(Value x) { 34 return x.keys(); 35 } 36 37 static Value front(Value x) { 38 return x[0]; 39 } 40 41 static Value back(Value x) { 42 return x[x.length - 1]; 43 } 44 45 static Value get(Value x, Value key, Value def) { 46 Value result; 47 if (x.has(key, &result)) 48 return result; 49 return def; 50 } 51 52 static Value def(Value x, Value def) { 53 if ((x.type == Value.Type.Undefined) || (x.type == Value.Type.Null)) 54 return def; 55 return x; 56 } 57 58 static long tointeger(Value x) { 59 return x.get!long; 60 } 61 62 static double tofloat(Value x) { 63 return x.get!double; 64 } 65 66 static string tostring(Value x) { 67 return x.toString(); 68 } 69 70 static bool tobool(Value x) { 71 return x.get!bool; 72 } 73 74 static string escape(Value[] args) { 75 auto value = args[0].get!string; 76 77 foreach (f; args[1..$]) { 78 switch (f.get!string) { 79 case "html": 80 value = escapeHTML(value); 81 break; 82 case "js": 83 value = escapeJS(value); 84 break; 85 case "uri": 86 value = escapeURI(value); 87 break; 88 default: 89 assert("unimplemented escape filter: " ~ f.get!string); 90 break; 91 } 92 } 93 94 return value; 95 } 96 97 static string translate(Value[] args) { 98 return args[0].get!string; 99 } 100 101 globals["length"] = Value(&length); 102 globals["empty"] = Value(&empty); 103 globals["keys"] = Value(&keys); 104 globals["front"] = Value(&front); 105 globals["back"] = Value(&back); 106 107 globals["integer"] = Value(&tointeger); 108 globals["float"] = Value(&tofloat); 109 globals["string"] = Value(&tostring); 110 globals["bool"] = Value(&tobool); 111 112 globals["get"] = Value(&get); 113 globals["default"] = Value(&def); 114 115 globals["escape"] = Value(&escape); 116 117 globals["__escape"] = Value(&escape); 118 globals["__translate"] = Value(&translate); 119 } 120 121 122 void bindLibMath(ref Value[string] globals) { 123 import std.math; 124 125 static Value abs(Value x) { 126 final switch (x.type) with (Value.Type) { 127 case Undefined: 128 case Function: 129 case String: 130 case Array: 131 case AssocArray: 132 case Object: 133 case Pointer: 134 case Null: 135 case Bool: 136 return x; 137 case Integer: 138 return Value(std.math.abs(x.get!long)); 139 case Float: 140 return Value(std.math.abs(x.get!double)); 141 } 142 } 143 144 globals["abs"] = Value(&abs); 145 } 146 147 148 void bindLibString(ref Value[string] globals) { 149 static string join(Value[] x) { 150 auto app = appender!string; 151 auto value = x[0]; 152 auto len = value.length; 153 auto seps = (x.length > 1) ? x[1].get!string : ","; 154 foreach (size_t i, v; value) { 155 app.put(v.get!string); 156 if (i + 1 != len) 157 app.put(seps); 158 } 159 return app.data; 160 } 161 162 static string[] split(Value[] x) { 163 auto value = x[0].get!string; 164 auto sep = (x.length > 1) ? x[1].get!string : " "; 165 166 return value.split(sep); 167 } 168 169 static string strip(Value x) { 170 return x.get!string.strip; 171 } 172 173 static string lower(Value x) { 174 return x.get!string.toLower(); 175 } 176 177 static string upper(Value x) { 178 return x.get!string.toUpper(); 179 } 180 181 static long indexOf(Value[] args) { 182 auto haystack = args[0].get!string; 183 auto needle = args[1].get!string; 184 auto start = (args.length > 2) ? args[2].get!size_t : 0; 185 186 return haystack.indexOf(needle, start); 187 } 188 189 static string format(Value[] args) { 190 auto fmt = args[0].get!string; 191 auto spec = FormatSpec!char(fmt); 192 193 auto app = appender!string; 194 app.reserve(max(32, fmt.length + fmt.length >> 1)); 195 196 size_t arg = 1; 197 while (spec.writeUpToNextSpec(&app)) { 198 if (arg >= args.length) 199 break; 200 201 auto value = args[arg++]; 202 final switch (value.type) with (Value.Type) { 203 case Undefined: 204 case Function: 205 case String: 206 case Array: 207 case AssocArray: 208 case Object: 209 case Pointer: 210 formatValue(&app, value.get!string, spec); 211 break; 212 case Null: 213 formatValue(&app, null, spec); 214 break; 215 case Bool: 216 formatValue(&app, value.get!bool, spec); 217 break; 218 case Integer: 219 formatValue(&app, value.get!long, spec); 220 break; 221 case Float: 222 formatValue(&app, value.get!double, spec); 223 break; 224 } 225 } 226 227 if (arg != args.length) 228 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)); 229 return app.data; 230 } 231 232 globals["join"] = Value(&join); 233 globals["split"] = Value(&split); 234 globals["format"] = Value(&format); 235 globals["strip"] = Value(&strip); 236 globals["lower"] = Value(&lower); 237 globals["upper"] = Value(&upper); 238 globals["indexOf"] = Value(&indexOf); 239 } 240 241 242 void bindLibDefault(ref Value[string] globals) { 243 bindLibBasic(globals); 244 bindLibString(globals); 245 bindLibMath(globals); 246 } 247 248 249 string escapeHTML(string x) { 250 auto app = appender!string; 251 app.reserve(8 + x.length + (x.length >> 1)); 252 253 foreach (ch; x.byDchar) { 254 switch (ch) { 255 case '"': 256 app.put("""); 257 break; 258 case '\'': 259 app.put("'"); 260 break; 261 case 'a': .. case 'z': 262 goto case; 263 case 'A': .. case 'Z': 264 goto case; 265 case '0': .. case '9': 266 goto case; 267 case ' ', '\t', '\n', '\r', '-', '_', '.', ':', ',', ';', 268 '#', '+', '*', '?', '=', '(', ')', '/', '!', 269 '%' , '{', '}', '[', ']', '$', '^', '~': 270 app.put(cast(char)ch); 271 break; 272 case '<': 273 app.put("<"); 274 break; 275 case '>': 276 app.put(">"); 277 break; 278 case '&': 279 app.put("&"); 280 break; 281 default: 282 formattedWrite(&app, "&#x%02X;", cast(uint)ch); 283 break; 284 } 285 } 286 return app.data; 287 } 288 289 290 string escapeJS(string x) { 291 auto app = appender!string; 292 app.reserve(x.length + (x.length >> 1)); 293 294 foreach (ch; x.byDchar) { 295 switch (ch) { 296 case '\\': 297 app.put(`\\`); 298 break; 299 case '\'': 300 app.put(`\'`); 301 break; 302 case '\"': 303 app.put(`\"`); 304 break; 305 case '\r': 306 break; 307 case '\n': 308 app.put(`\n`); 309 break; 310 default: 311 app.put(ch); 312 break; 313 } 314 } 315 return app.data; 316 } 317 318 319 string escapeURI(string x) { 320 auto app = appender!string; 321 app.reserve(8 + x.length + (x.length >> 1)); 322 323 foreach (i; 0..x.length) { 324 switch (x.ptr[i]) { 325 case 'A': .. case 'Z': 326 case 'a': .. case 'z': 327 case '0': .. case '9': 328 case '-': case '_': case '.': case '~': 329 app.put(x.ptr[i]); 330 break; 331 default: 332 formattedWrite(&app, "%%%02X", x.ptr[i]); 333 break; 334 } 335 } 336 337 return app.data; 338 }