145 | 145 |
* //{{^repo}}No repos :({{/repo}}
|
146 | 146 |
* // to
|
147 | 147 |
* //No repos :(
|
148 | |
* context["foo"] = "bar"; // not set to "repo"
|
|
148 |
* context["foo"] = "bar"; // not set to "repo"
|
149 | 149 |
* -----
|
150 | 150 |
*/
|
151 | 151 |
static final class Context
|
|
216 | 216 |
return !list.length;
|
217 | 217 |
}
|
218 | 218 |
}
|
219 | |
|
|
219 |
|
220 | 220 |
/* Convenience function */
|
221 | 221 |
@safe @property
|
222 | 222 |
static Section nil() nothrow
|
|
234 | 234 |
|
235 | 235 |
public:
|
236 | 236 |
@safe
|
237 | |
this(in Context context = null) nothrow
|
|
237 |
this(const Context context = null) nothrow
|
238 | 238 |
{
|
239 | 239 |
parent = context;
|
240 | 240 |
}
|
|
332 | 332 |
* size = reserve size for avoiding reallocation
|
333 | 333 |
*
|
334 | 334 |
* Returns:
|
335 | |
* new Context object that added to $(D_PARAM key) section list.
|
|
335 |
* new Context object that added to $(D_PARAM key) section list.
|
336 | 336 |
*/
|
337 | 337 |
@trusted
|
338 | 338 |
Context addSubContext(in String key, lazy size_t size = 1)
|
|
356 | 356 |
*
|
357 | 357 |
* Params:
|
358 | 358 |
* key = key string to fetch
|
359 | |
*
|
|
359 |
*
|
360 | 360 |
* Returns:
|
361 | 361 |
* a $(D_PARAM key) associated value. null if key does not exist.
|
362 | 362 |
*/
|
|
364 | 364 |
String fetch(in String[] key, lazy Handler handler = null) const
|
365 | 365 |
{
|
366 | 366 |
assert(key.length > 0);
|
367 | |
|
|
367 |
|
368 | 368 |
if (key.length == 1) {
|
369 | 369 |
auto result = key[0] in variables;
|
370 | 370 |
|
|
382 | 382 |
return *result;
|
383 | 383 |
}
|
384 | 384 |
}
|
385 | |
|
|
385 |
|
386 | 386 |
return handler is null ? null : handler()(keyToString(key));
|
387 | 387 |
}
|
388 | 388 |
|
|
390 | 390 |
const(Section) fetchSection()(in String[] key) const /* nothrow */
|
391 | 391 |
{
|
392 | 392 |
assert(key.length > 0);
|
393 | |
|
|
393 |
|
394 | 394 |
// Ascend context tree to find the key's beginning
|
395 | 395 |
auto currentSection = key[0] in sections;
|
396 | 396 |
if (currentSection is null) {
|
|
399 | 399 |
|
400 | 400 |
return parent.fetchSection(key);
|
401 | 401 |
}
|
402 | |
|
|
402 |
|
403 | 403 |
// Decend context tree to match the rest of the key
|
404 | 404 |
size_t keyIndex = 0;
|
405 | 405 |
while (currentSection) {
|
406 | 406 |
// Matched the entire key?
|
407 | 407 |
if (keyIndex == key.length-1)
|
408 | 408 |
return currentSection.empty ? Section.nil : *currentSection;
|
409 | |
|
|
409 |
|
410 | 410 |
if (currentSection.type != SectionType.list)
|
411 | 411 |
return Section.nil; // Can't decend any further
|
412 | |
|
|
412 |
|
413 | 413 |
// Find next part of key
|
414 | 414 |
keyIndex++;
|
415 | 415 |
foreach (c; currentSection.list)
|
|
429 | 429 |
auto result = fetchSection(key);
|
430 | 430 |
if (result.type == type)
|
431 | 431 |
return result.empty ? null : mixin("result." ~ to!string(type));
|
432 | |
|
|
432 |
|
433 | 433 |
return null;
|
434 | 434 |
}
|
435 | 435 |
|
|
629 | 629 |
render(name, context, sink);
|
630 | 630 |
return sink.data;
|
631 | 631 |
}
|
632 | |
|
|
632 |
|
633 | 633 |
/**
|
634 | 634 |
* OutputRange version of $(D render).
|
635 | 635 |
*/
|
|
709 | 709 |
static void encode(in String text, ref Sink sink)
|
710 | 710 |
{
|
711 | 711 |
size_t index;
|
712 | |
|
|
712 |
|
713 | 713 |
foreach (i, c; text) {
|
714 | 714 |
String temp;
|
715 | 715 |
|
|
943 | 943 |
sTag = src[0..i];
|
944 | 944 |
eTag = src[i + 1..$].stripLeft();
|
945 | 945 |
}
|
946 | |
|
|
946 |
|
947 | 947 |
size_t getEnd(String src)
|
948 | 948 |
{
|
949 | 949 |
auto end = src.indexOf(eTag);
|
950 | 950 |
if (end == -1)
|
951 | 951 |
throw new MustacheException("Mustache tag is not closed");
|
952 | |
|
|
952 |
|
953 | 953 |
return end;
|
954 | 954 |
}
|
955 | |
|
|
955 |
|
956 | 956 |
// State capturing for section
|
957 | 957 |
struct Memo
|
958 | 958 |
{
|
959 | 959 |
String[] key;
|
960 | 960 |
Node[] nodes;
|
961 | 961 |
String source;
|
962 | |
|
|
962 |
|
963 | 963 |
bool opEquals()(auto ref const Memo m) inout
|
964 | 964 |
{
|
965 | 965 |
// Don't compare source because the internal
|
|
1046 | 1046 |
case '{':
|
1047 | 1047 |
src = src[1..$];
|
1048 | 1048 |
auto key = parseKey(src, "}", end);
|
1049 | |
|
|
1049 |
|
1050 | 1050 |
end += 1;
|
1051 | 1051 |
if (end >= src.length || !src[end..$].startsWith(eTag))
|
1052 | 1052 |
throw new MustacheException("Unescaped tag is not closed");
|
1053 | |
|
|
1053 |
|
1054 | 1054 |
result ~= Node(NodeType.var, key, true);
|
1055 | 1055 |
break;
|
1056 | 1056 |
case '&':
|
|
1136 | 1136 |
beforeEatWSIndex = index;
|
1137 | 1137 |
index = src.length - src[index..$].stripLeft().length;
|
1138 | 1138 |
}
|
1139 | |
|
|
1139 |
|
1140 | 1140 |
void acceptKeySegment()
|
1141 | 1141 |
{
|
1142 | 1142 |
if (keySegmentStart >= beforeEatWSIndex)
|
|
1144 | 1144 |
|
1145 | 1145 |
key ~= src[keySegmentStart .. beforeEatWSIndex];
|
1146 | 1146 |
}
|
1147 | |
|
|
1147 |
|
1148 | 1148 |
eatWhitespace();
|
1149 | 1149 |
keySegmentStart = index;
|
1150 | 1150 |
|
|
1163 | 1163 |
eatWhitespace();
|
1164 | 1164 |
}
|
1165 | 1165 |
}
|
1166 | |
|
|
1166 |
|
1167 | 1167 |
end = index;
|
1168 | 1168 |
return key;
|
1169 | 1169 |
}
|
|
1264 | 1264 |
} catch (MustacheException e) { }
|
1265 | 1265 |
}
|
1266 | 1266 |
}
|
1267 | |
|
|
1267 |
|
1268 | 1268 |
@trusted
|
1269 | 1269 |
static String keyToString(in String[] key)
|
1270 | 1270 |
{
|
1271 | 1271 |
if (key.length == 0)
|
1272 | 1272 |
return null;
|
1273 | |
|
|
1273 |
|
1274 | 1274 |
if (key.length == 1)
|
1275 | 1275 |
return key[0];
|
1276 | |
|
|
1276 |
|
1277 | 1277 |
Appender!String buf;
|
1278 | 1278 |
foreach (index, segment; key) {
|
1279 | 1279 |
if (index != 0)
|
1280 | 1280 |
buf.put('.');
|
1281 | |
|
|
1281 |
|
1282 | 1282 |
buf.put(segment);
|
1283 | 1283 |
}
|
1284 | |
|
|
1284 |
|
1285 | 1285 |
return buf.data;
|
1286 | 1286 |
}
|
1287 | |
|
|
1287 |
|
1288 | 1288 |
/*
|
1289 | 1289 |
* Mustache's node types
|
1290 | 1290 |
*/
|
|
1356 | 1356 |
string toString() const
|
1357 | 1357 |
{
|
1358 | 1358 |
string result;
|
1359 | |
|
|
1359 |
|
1360 | 1360 |
final switch (type) {
|
1361 | 1361 |
case NodeType.text:
|
1362 | 1362 |
result = "[T : \"" ~ to!string(text) ~ "\"]";
|