1 module generator; 2 3 import std.array; 4 import std.stdio; 5 import std.string; 6 import std.typecons; 7 import std.json; 8 import std.algorithm; 9 import std.format; 10 import std.conv : to; 11 import transforms.camel; 12 import transforms.snake; 13 import iban; 14 15 class Generator { 16 import fakerjsgenerator; 17 string output; 18 string[] toOverride; 19 20 this(string locale, string fallback, string[] toOverride) { 21 this.toOverride = toOverride; 22 if(locale == "en") { 23 this.output = ` 24 /// 25 module faked.base; 26 27 import std.datetime; 28 import std.exception : enforce; 29 30 struct Currency { 31 @safe: 32 string name; 33 string code; 34 string symbol; 35 36 @property bool empty() const { 37 return name.length == 0; 38 } 39 } 40 41 /// 42 struct BBan { 43 string type; 44 long count; 45 } 46 47 /// 48 struct IbanFormat { 49 string country; 50 long total; 51 BBan[] bban; 52 string format; 53 } 54 55 /// 56 struct IbanData { 57 dchar[] alpha; 58 string[] pattern10; 59 string[] pattern100; 60 IbanFormat[] formats; 61 string[] iso3166; 62 } 63 64 /// 65 class Faker { 66 @safe: 67 import std.random; 68 import std.array; 69 import std.format; 70 import std.conv : to; 71 import std.string : toUpper; 72 import std.range : iota, take, repeat; 73 import std.algorithm : map, joiner; 74 75 /// 76 Random rnd; 77 78 /// 79 this(int seed) { 80 this.rnd = Random(seed); 81 } 82 83 /// 84 string addressLatitude() { 85 return to!string(uniform(-90.0, 90.0, this.rnd)); 86 } 87 88 /// 89 string addressLongitude() { 90 return to!string(uniform(-90.0, 90.0, this.rnd)); 91 } 92 93 /// 94 string fianceAccount(size_t length = 8) { 95 string s; 96 foreach(i; 0 .. length) { 97 s ~= "#"; 98 } 99 return digitBuild(s); 100 } 101 102 /// 103 string fianceRoutingNumber() { 104 import std.conv : to; 105 import std.math : ceil; 106 auto routingNumber = digitBuild("########"); 107 108 // Modules 10 straight summation. 109 size_t sum = 0; 110 111 for(size_t i = 0; i < routingNumber.length; i += 3) { 112 sum += to!size_t(routingNumber[i]) * 3; 113 sum += to!size_t(routingNumber[i + 1]) * 7; 114 if(i + 2 < routingNumber.length) { 115 sum += to!size_t(routingNumber[i + 2]); 116 } else { 117 sum += 0; 118 } 119 } 120 121 return routingNumber ~ to!string((ceil(sum / 10.0) * 10 - sum)); 122 } 123 124 /// 125 string financeMask(size_t length = 4, bool parents = true, 126 bool ellipsis = true) 127 { 128 import std.algorithm : joiner; 129 import std.conv : to; 130 string tmp = ""; 131 132 for(size_t i = 0; i < length; i++) { 133 tmp ~= '#'; 134 } 135 136 //prefix with ellipsis 137 tmp = ellipsis ? ["...", tmp].joiner("").to!string() : tmp; 138 139 tmp = parents ? ["(", tmp, ")"].joiner("").to!string() : tmp; 140 141 //generate random numbers 142 tmp = digitBuild(tmp); 143 144 return tmp; 145 } 146 147 /// 148 string financeBitcoinAddress() { 149 import std.conv : to; 150 static enum data = 151 to!(dchar[])( 152 "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ" 153 ); 154 155 string ret = to!string(choice([1, 3], this.rnd)); 156 foreach(it; 0 .. choice([25,35], this.rnd)) { 157 ret ~= choice(data, this.rnd); 158 } 159 return ret; 160 } 161 162 /// 163 string loremSentance(size_t length = size_t.max) { 164 import std.algorithm : copy; 165 length = length == size_t.max || length == 0 166 ? uniform(3, 10, this.rnd) 167 : length; 168 auto app = appender!string(); 169 copy(iota(length).map!(a => loremWords).joiner(" "), app); 170 //foreach(it; 0 .. length) { 171 // app.put(loremWords()); 172 // app.put(" "); 173 //} 174 switch(uniform(0, 15, this.rnd)) { 175 case 0: app.put("!"); break; 176 case 1: app.put("?"); break; 177 default: app.put("."); break; 178 } 179 180 string ret = app.data; 181 string f = to!string(toUpper(ret.front)); 182 ret.popFront(); 183 return f ~ ret; 184 } 185 186 /// 187 string loremSentances(size_t length = size_t.max) { 188 import std.algorithm : map, joiner; 189 import std.range : iota; 190 import std.conv : to; 191 length = length == size_t.max || length == 0 192 ? uniform(2, 6, this.rnd) 193 : length; 194 195 return iota(length) 196 .map!(a => loremSentance()) 197 .joiner(" ") 198 .to!string(); 199 } 200 201 /// 202 string loremParagraph(size_t length = size_t.max) { 203 length = length == size_t.max || length == 0 204 ? uniform(2, 6, this.rnd) 205 : length; 206 207 return loremSentances(length + uniform(0, 3, this.rnd)); 208 } 209 210 /// 211 string loremParagraphs(size_t length = size_t.max) { 212 import std.algorithm : map, joiner; 213 import std.range : iota; 214 215 length = length == size_t.max || length == 0 216 ? uniform(2, 6, this.rnd) 217 : length; 218 return iota(length) 219 .map!(a => loremParagraph()) 220 .joiner("\n") 221 .to!string(); 222 } 223 224 /// 225 string loremText(size_t length = size_t.max) { 226 length = length == size_t.max || length == 0 227 ? uniform(2, 6, this.rnd) 228 : length; 229 230 auto app = appender!string(); 231 foreach(it; 0 .. length) { 232 switch(uniform(0, 4, this.rnd)) { 233 case 0: 234 app.put(loremWords()); 235 continue; 236 case 1: 237 app.put(loremParagraph()); 238 continue; 239 case 2: 240 app.put(loremSentance()); 241 continue; 242 case 3: 243 app.put(loremSentances()); 244 continue; 245 default: 246 assert(false); 247 } 248 } 249 250 return app.data(); 251 } 252 253 /// 254 string phoneNumber() { 255 return this.digitBuild(this.phoneNumberFormats()); 256 } 257 258 /// 259 string commerceProductName() { 260 return this.commerceProductNameAdjective() ~ 261 this.commerceProductNameMaterial() ~ " " ~ 262 this.commerceProductNameProduct(); 263 } 264 265 /// 266 string companyCatchPhrase() { 267 return companyAdjective() ~ " " 268 ~ companyDescriptor() ~ " " 269 ~ companyNoun(); 270 } 271 272 // 273 string companyBs() { 274 return companyBsVerb() ~ " " ~ companyBsAdjective() ~ " " ~ 275 companyBsNoun(); 276 } 277 278 /// 279 string internetUserName(string firstname = "", string lastname = "") { 280 firstname = firstname.empty ? this.nameFirstName() : firstname; 281 lastname = lastname.empty ? this.nameLastName() : lastname; 282 283 string ret; 284 285 switch(uniform(0, 3, this.rnd)) { 286 case 0: 287 ret = firstname ~ to!string(uniform(0, 100, this.rnd)); 288 break; 289 case 1: 290 ret = firstname ~ choice([".", "_"], this.rnd) ~ lastname; 291 break; 292 case 2: 293 ret = firstname ~ choice([".", "_"], this.rnd) ~ lastname 294 ~ to!string(uniform(0, 100, this.rnd)); 295 break; 296 default: 297 assert(false); 298 } 299 300 return ret.replace("'", "").replace(" ", ""); 301 } 302 303 /// 304 string internetProtocol() { 305 return choice(["http", "https"], this.rnd); 306 } 307 308 /// 309 string internetDomainWord() { 310 import std.uni : isAlphaNum; 311 import std.utf : byDchar; 312 import std.algorithm : filter; 313 314 return this.nameFirstName() 315 .byDchar() 316 .filter!(a => isAlphaNum(a)) 317 .to!string(); 318 } 319 320 /// 321 string internetDomainName() { 322 return this.internetDomainWord() ~ "." ~ this.internetDomainSuffix(); 323 } 324 325 /// 326 string internetUrl() { 327 return this.internetProtocol() ~ "://" ~ this.internetDomainName(); 328 } 329 330 /// 331 string internetIPv4() { 332 int[4] t; 333 foreach(i; 0 .. t.length) { 334 t[i] = uniform(0, 256, this.rnd); 335 } 336 337 return t[].map!(a => to!string(a)).joiner(".").to!string(); 338 } 339 340 /// 341 string internetIPv6() { 342 static enum elem = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", 343 "a", "b", "c", "d", "e", "f"]; 344 345 return iota(8) 346 .map!(a => randomCover(elem, this.rnd).take(4).to!string()) 347 .joiner(".") 348 .to!string(); 349 } 350 351 /// 352 string internetColor(int baseRed255 = 0, int baseGreen255 = 0, 353 int baseBlue255 = 0) 354 { 355 int red = to!int((uniform(0, 256, this.rnd) + baseRed255) / 2); 356 int green = to!int((uniform(0, 256, this.rnd) + baseGreen255) / 2); 357 int blue = to!int((uniform(0, 256, this.rnd) + baseBlue255) / 2); 358 string redStr = red.to!string(16); 359 string greenStr = green.to!string(16); 360 string blueStr = blue.to!string(16); 361 return "#" ~ 362 (redStr.length == 1 ? "0" : "") ~ redStr ~ 363 (greenStr.length == 1 ? "0" : "") ~ greenStr ~ 364 (blueStr.length == 1 ? "0": "") ~ blueStr; 365 } 366 367 /// 368 string internetPassword(bool strong = false) { 369 return strong ? "Password" : "password"; 370 } 371 372 /// 373 string vehicle() { 374 return this.vehicleManufacturer() ~ " " ~ this.vehicleModel(); 375 } 376 377 /// 378 string vehicleVin() { 379 return (this.helperAlphaNum(10) ~ this.helperAlpha(1, true) 380 ~ this.helperAlphaNum(1) 381 ~ to!string(uniform(10000, 100000, this.rnd)) 382 ).toUpper(); 383 } 384 385 /// 386 string helperAlpha(size_t count = 1, bool upperCase = false) @trusted { 387 static enum data = to!(dchar[])(['a', 'b', 'c', 'd', 'e', 'f', 'g', 388 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 389 'u', 'v', 'w', 'x', 'y', 'z']); 390 391 return iota(count).map!(a => choice(data, this.rnd)).to!string(); 392 } 393 394 /// 395 string helperAlphaNum(size_t count = 1) @trusted { 396 static enum data = to!(dchar[])(['0', '1', '2', '3', '4', '5', '6', '7', 397 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 398 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 399 'y', 'z']); 400 return iota(count).map!(a => choice(data, this.rnd)).to!string(); 401 } 402 403 /// 404 string helperHexaDecimal(size_t count = 1) @trusted { 405 static enum data = to!(dchar[])(['0', '1', '2', '3', '4', '5', '6', 406 '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 407 'E', 'F']); 408 409 return iota(count).map!(a => choice(data, this.rnd)).to!string(); 410 } 411 412 /// 413 string passportNumber() { 414 return helperHexaDecimal(9); 415 } 416 417 /// 418 DateTime datePast(size_t years = 10, DateTime refDate = 419 cast(DateTime)Clock.currTime()) 420 { 421 return refDate + dur!"minutes"(-uniform(0, years * 365 * 24 * 60, 422 this.rnd)); 423 } 424 425 /// 426 DateTime dateFuture(size_t years = 10, DateTime refDate = 427 cast(DateTime)Clock.currTime()) 428 { 429 return refDate + dur!"minutes"(uniform(0, years * 365 * 24 * 60, 430 this.rnd)); 431 } 432 433 /// 434 DateTime dateBetween(DateTime begin, DateTime end) { 435 enforce(begin <= end, "begin must be <= end"); 436 Duration d = end - begin; 437 long hours = d.total!"hours"(); 438 return begin + dur!("hours")(uniform(0, hours, this.rnd)); 439 } 440 441 /// 442 string ukNationalInsuranceNumber() { 443 auto app = appender!string(); 444 445 static enum data = 446 to!(dchar[])( 447 "ABCEGHJKLMNPRSTUWXYZ" 448 ); 449 450 static enum suffix = 451 to!(dchar[])( 452 "ABCD" 453 ); 454 455 formattedWrite(app, "%s%s", choice(data, this.rnd), choice(data, this.rnd)); 456 formattedWrite(app, "%06d", uniform(0, 1_000_000, this.rnd)); 457 formattedWrite(app, "%02d", uniform(0, 100, this.rnd)); 458 formattedWrite(app, "%s", choice(suffix, this.rnd)); 459 return app.data; 460 } 461 462 /// 463 string nameGenderBinary() { 464 return choice(["Man", "Woman"], this.rnd); 465 } 466 467 `; 468 469 this.output ~= format("\tstatic IbanData ibanData = %s;", 470 buildIbanData() 471 ); 472 } else { 473 this.output = format(`/// 474 module faked.faker_%1$s; 475 476 import faked.base; 477 %3$s 478 479 /// 480 class Faker_%1$s : Faker%2$s { 481 @safe: 482 import std.random; 483 import std.array; 484 import std.format; 485 import std.conv : to; 486 487 /// 488 this(int seed) { 489 super(seed); 490 } 491 492 `, 493 toLower(locale), (fallback.empty || fallback == "en") ? "" 494 : "_" ~ fallback, 495 (fallback.empty || fallback == "en") ? "" 496 : format("import faked.faker_%s;", fallback) 497 ); 498 } 499 500 if(locale == "en") { 501 this.output ~= ` 502 /// 503 string digitBuild(string s, dchar sym = '#') { 504 auto app = appender!string(); 505 for(size_t idx = 0; idx < s.length; ++idx) { 506 dchar c = s[idx]; 507 if(c == sym) { 508 formattedWrite(app, "%d", uniform(0, 10, this.rnd)); 509 } else if(c == '[') { 510 ++idx; 511 size_t start = idx; 512 while(idx < s.length && s[idx] != ']') { 513 ++idx; 514 } 515 enforce(idx < s.length && s[idx] == ']'); 516 string[] ft = s[start .. idx].split("-"); 517 enforce(ft.length == 2); 518 int[] ftI = ft.map!(a => to!int(a)).array; 519 enforce(ft.length == 2); 520 int n = uniform(ftI[0], ftI[1], this.rnd); 521 formattedWrite(app, "%d", n); 522 } else if(c == '!') { 523 formattedWrite(app, "%d", uniform(2, 10, this.rnd)); 524 } else { 525 app.put(c); 526 } 527 } 528 return app.data; 529 } 530 531 /// 532 string replaceChars(string s) { 533 static enum alpha = to!(dchar[])("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); 534 auto app = appender!string(); 535 foreach(dchar c; s) { 536 if(c == '#') { 537 formattedWrite(app, "%d", choice(alpha, this.rnd)); 538 } else { 539 app.put(c); 540 } 541 } 542 return app.data; 543 } 544 545 /// 546 string replaceSymbols(string str) { 547 static enum alpha = to!(dchar[])([ 548 'A','B','C','D','E','F','G','H','I','J','K','L', 549 'M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z' 550 ]); 551 552 auto app = appender!string(); 553 554 foreach(dchar c; str) { 555 if(c == '#') { 556 formattedWrite(app, "%d", uniform(0, 10, this.rnd)); 557 } else if(c == '?') { 558 app.put(choice(alpha, this.rnd)); 559 } else if(c == '*') { 560 formattedWrite(app, "%s", 561 (uniform(0, 2) == 1) 562 ? to!string(choice(alpha, this.rnd)) 563 : to!string(uniform(0, 10, this.rnd)) 564 ); 565 } else { 566 app.put(c); 567 } 568 } 569 return app.data; 570 } 571 `; 572 } else { 573 } 574 } 575 576 void finish() { 577 this.output ~= "}\n"; 578 } 579 580 string getOverrideString(string name) { 581 return canFind(this.toOverride, name) ? "override " : ""; 582 } 583 584 string buildStringImpl(string name, string data, string retType = "string") { 585 string n = format("\t///\n\t%s%s %s() {\n" ~ 586 "\t\tstatic enum data = [\n\t\t%s\n\t\t];\n" ~ 587 "\t\treturn choice(data, this.rnd);\n" ~ 588 "\t}\n\n", this.getOverrideString(name), retType, name, data); 589 this.output ~= n; 590 return name; 591 } 592 593 string[] buildNameTitle(string data) { 594 string[] ret; 595 JSONValue js = parseJson(data); 596 597 foreach(key; ["descriptor", "job", "level"]) { 598 if(const(JSONValue)* value = key in js) { 599 string n = ("name_title_" ~ key).camelCase(); 600 ret ~= this.buildStringImpl(n, 601 value.array().map!(a => "\"" ~ a.str() ~ "\"") 602 .joiner(",\n\t\t").to!string() 603 ); 604 } 605 } 606 607 return ret; 608 } 609 610 string buildFinanceCurrency(string data) { 611 JSONValue js = parseJson(data); 612 auto jsO = js.object(); 613 return this.buildStringImpl("financeCurrency", 614 jsO.keys() 615 .map!(a => tuple(a, jsO[a])) 616 .filter!(a => "code" in a[1] && "symbol" in a[1]) 617 .map!(a => format("Currency(\"%s\", \"%s\", \"%s\")", 618 a[0], a[1]["code"].str(), a[1]["symbol"].str()) 619 ).joiner(",\n\t\t").to!string() 620 , "Currency" 621 ); 622 } 623 624 string[] buildIbanAndBic() { 625 string tmp =` 626 /// 627 string financeBIC() { 628 enum string[] vowels = ["A", "E", "I", "O", "U"]; 629 int prob = uniform(0, 100, this.rnd); 630 return this.replaceSymbols("???") ~ 631 choice(vowels, this.rnd) ~ 632 choice(ibanData.iso3166, this.rnd) ~ 633 this.replaceSymbols("?") ~ "1" ~ 634 (prob < 10 ? 635 this.replaceSymbols("?" ~ choice(vowels, this.rnd) ~ "?") : 636 prob < 40 ? 637 this.replaceSymbols("###") : ""); 638 } 639 640 /// 641 long mod97(string digitStr) { 642 long m = 0; 643 for(long i = 0; i < digitStr.length; i++) { 644 m = ((m * 10) + (digitStr[i] | 0)) % 97; 645 } 646 return m; 647 } 648 649 /// 650 string toDigitString(string str) { 651 import std.uni; 652 auto app = appender!string(); 653 foreach(dchar c; str) { 654 switch(c) { 655 case 'a': .. case 'z': 656 formattedWrite(app, "%s", Grapheme(toUpper(c))[0] - 55); 657 break; 658 case 'A': .. case 'Z': 659 formattedWrite(app, "%s", Grapheme(c)[0] - 55); 660 break; 661 default: 662 app.put(c); 663 break; 664 } 665 } 666 return app.data; 667 //return str.replace(/[A-Z]/gi, function(match) { 668 // return match.toUpperCase().charCodeAt(0) - 55; 669 //}); 670 } 671 672 /// TODO IBAN generation looks broken 673 string financeIBAN(bool fourSpace = false) { 674 auto ibanFormat = choice(ibanData.formats, this.rnd); 675 auto app = appender!string(); 676 string s; 677 int count = 0; 678 for(int b = 0; b < ibanFormat.bban.length; ++b) { 679 auto bban = ibanFormat.bban[b]; 680 long c = bban.count; 681 count += bban.count; 682 while(c > 0) { 683 if(bban.type == "a") { 684 //s += faker.random.arrayElement(ibanLib.alpha); 685 app.put(choice(ibanData.alpha, this.rnd)); 686 } else if (bban.type == "c") { 687 if (uniform(0, 100, this.rnd) < 80) { 688 formattedWrite(app, "%d", uniform(0, 10, this.rnd)); 689 //s += faker.random.number(9); 690 } else { 691 app.put(choice(ibanData.alpha, this.rnd)); 692 //s += faker.random.arrayElement(ibanLib.alpha); 693 } 694 } else { 695 if (c >= 3 && uniform(0, 101, this.rnd) < 30) { 696 //if (c >= 3 && faker.random.number(100) < 30) { 697 if (uniform(0, 2, this.rnd) == 1) { 698 //s += faker.random.arrayElement(ibanLib.pattern100); 699 app.put(choice(ibanData.pattern100, this.rnd)); 700 c -= 2; 701 } else { 702 //s += faker.random.arrayElement(ibanLib.pattern10); 703 app.put(choice(ibanData.pattern10, this.rnd)); 704 c--; 705 } 706 } else { 707 //s += faker.random.number(9); 708 formattedWrite(app, "%d", uniform(0, 10, this.rnd)); 709 } 710 } 711 c--; 712 } 713 s = app.data[0 .. count]; 714 } 715 auto checksum = 98 - mod97(toDigitString(s ~ ibanFormat.country ~ "00")); 716 string checksumStr; 717 if (checksum < 10) { 718 checksumStr = "0" ~ to!string(checksum); 719 } 720 auto iban = ibanFormat.country ~ checksumStr ~ s; 721 return fourSpace 722 ? format("%,4?s", ' ', iban) 723 : iban; 724 } 725 `; 726 727 this.output ~= tmp; 728 729 //return ["financeIBAN", "financeBIC"]; 730 return ["financeBIC", "financeIBAN"]; 731 } 732 733 string[] buildCreditCards(Sub sub) { 734 import jssplitter; 735 736 string[] ret; 737 738 string tmp = ` 739 /// 740 string fianaceCreditCardCVV() { 741 string ret; 742 for(int i = 0; i < 3; ++i) { 743 ret ~= to!string(uniform(0, 3, this.rnd)); 744 } 745 return ret; 746 } 747 `; 748 tmp ~= format(` 749 /// 750 string financeCreditCard() { 751 switch(uniform(0, %s, this.rnd)) { 752 `, sub.subs.length - 2); 753 int cnt = 0; 754 foreach(key, value; sub.subs) { 755 if(key == "laser" || key == "maestro") { 756 continue; 757 } 758 tmp ~= format("\t\t\tcase " ~ to!string(cnt++) 759 ~ ": \n\t\t\t\treturn financeCreditCard%s();\n", 760 to!string(toUpper(key[0])) ~ key[1 .. $].camelCase()); 761 } 762 tmp ~= ` 763 default: 764 assert(false); 765 } 766 assert(false); 767 } 768 769 `; 770 this.output ~= tmp; 771 772 foreach(key, value; sub.subs) { 773 TypeLines tl = jssplit(value.data); 774 if(tl.type == Type.digit) { 775 string fname = "financeCreditCard" ~ to!string(toUpper(key[0])) 776 ~ key[1 .. $].camelCase(); 777 tmp = format("\t///\n\t%sstring %s() {\n", 778 this.getOverrideString(fname), fname 779 780 ); 781 tmp ~= format("\t\tstatic enum data = [\n\t\t%(%s,\n\t\t%)\n\t\t];\n", 782 tl.lines 783 ); 784 tmp ~= "\t\treturn this.digitBuild(choice(data, this.rnd));\n"; 785 tmp ~= "\t}\n\n"; 786 787 this.output ~= tmp; 788 ret ~= fname; 789 } else { 790 writefln("failed on %s with type %s", key, tl.type); 791 } 792 793 } 794 return ret ~ ["fianaceCreditCardCVV", "financeCreditCard"]; 795 } 796 797 string[] buildCommerceProductName(string data) { 798 string[] ret; 799 JSONValue js = parseJson(data); 800 auto jsO = js.object(); 801 foreach(key; ["adjective", "material", "product"]) { 802 if(const(JSONValue)* value = key in js) { 803 string n = ("commerce_product_name_" ~ key).camelCase(); 804 ret ~= this.buildStringImpl(n, 805 value.array().map!(a => "\"" ~ a.str() ~ "\"") 806 .joiner(",\n\t\t").to!string() 807 ); 808 } 809 } 810 return ret; 811 } 812 813 string buildString(string name, string postfix, string[] lines) { 814 string fname = name ~ "_" ~ postfix; 815 fname = fname.camelCase(); 816 817 this.buildStringImpl(fname, 818 lines.map!(a => "\"" ~ a ~ "\"").joiner(",\n\t\t").to!string() 819 ); 820 821 return fname; 822 } 823 824 string buildDigits(string name, string postfix, string[] lines) { 825 string fname = name ~ "_" ~ postfix; 826 fname = fname.camelCase(); 827 string n = format("\t///\n\t%sstring %s() {\n", 828 this.getOverrideString(fname), fname 829 ); 830 n ~= format("\t\tstatic enum data = [\n\t\t%(%s,\n\t\t%)\n\t\t];\n", 831 lines 832 ); 833 n ~= "\t\treturn this.digitBuild(choice(data, this.rnd));\n"; 834 n ~= "\t}\n\n"; 835 836 this.output ~= n; 837 return fname; 838 } 839 840 string buildCall(string name, string postfix, string[] lines) { 841 string fname = name ~ "_" ~ postfix; 842 fname = fname.camelCase(); 843 string n = format("\t///\n\t%sstring %s() {\n", this.getOverrideString(fname), 844 fname 845 ); 846 if(lines.length > 1) { 847 n ~= format("\t\tswitch(uniform(0, %d, this.rnd)) {\n", lines.length); 848 foreach(idx, l; lines) { 849 n ~= format("\t\t\tcase %d:\n", idx); 850 n ~= "\t\t\t\t" ~ buildCallImpl(l, name); 851 } 852 853 n ~= "\t\t\tdefault: assert(false);\n"; 854 n ~= "\t\t}\n"; 855 } else { 856 n ~= "\t\t" ~ buildCallImpl(lines[0], name); 857 } 858 n ~= "\t}\n\n"; 859 860 this.output ~= n; 861 return fname; 862 } 863 864 string buildCallImpl(string line, string prefix) { 865 string ret = "return format!\""; 866 bool inCall = false; 867 string tmp; 868 string[] calls; 869 while(!line.empty) { 870 if(inCall && line.front != '}') { 871 tmp ~= line.front; 872 line.popFront(); 873 } else if(inCall && line.front == '}') { 874 calls ~= tmp; 875 tmp = ""; 876 inCall = false; 877 line.popFront(); 878 ret ~= "%s"; 879 } else if(line.startsWith("#{")) { 880 assert(!inCall); 881 inCall = true; 882 line = line[2 .. $]; 883 } else { 884 ret ~= line.front; 885 line.popFront(); 886 } 887 } 888 ret ~= "\"("; 889 ret ~= to!string(calls 890 //.map!(a => prefix ~ "_" ~ to!char(toLower(a[0])) ~ a[1 ..$]) 891 .map!(a => callReplace(a, prefix)) 892 .map!(a => a ~ "()") 893 .joiner(", ")); 894 ret ~= ");\n"; 895 return ret; 896 } 897 898 string callReplace(string s, string prefix) { 899 if(s.indexOf(".") == -1) { 900 string t = (prefix ~ "_" ~ s.snakeCase()).camelCase(); 901 //string t = (prefix ~ to!char(toUpper(s[0])) ~ s[1 .. $]); 902 //t = t.camelCase(); 903 //writeln("nor ", s, " ", prefix, " ", t); 904 return t; 905 } else { 906 string t = s.snakeCase(); 907 t = t.replace(".", "_"); 908 t = t.camelCase(); 909 //writeln("dot ", t); 910 return t; 911 } 912 913 } 914 } 915 916 JSONValue parseJson(string input) { 917 input = input.strip(); 918 enum fStr = "module[\"exports\"] = "; 919 enum eStr = "};"; 920 if(!input.startsWith(fStr)) { 921 return parseJSON("{}"); 922 } 923 924 input = input[fStr.length .. $]; 925 926 if(!input.endsWith("};")) { 927 return parseJSON("{}"); 928 } 929 930 input = input[0 .. $ - 1]; 931 932 return parseJSON(input); 933 }