1 module graphql.builder; 2 3 import std.experimental.allocator; 4 import std.experimental.logger : logf; 5 import std.experimental.allocator.mallocator : Mallocator; 6 import std.exception : enforce; 7 8 import fixedsizearray; 9 10 import graphql.helper; 11 import graphql.ast; 12 import graphql.parser; 13 import graphql.lexer; 14 15 @safe: 16 17 Selections findFragment(Document doc, string name, string[] typenames) { 18 import std.algorithm.searching : canFind; 19 import std.experimental.logger : logf; 20 Definitions cur = doc.defs; 21 while(cur !is null) { 22 enforce(cur.def !is null); 23 if(cur.def.ruleSelection == DefinitionEnum.F) { 24 enforce(cur.def.frag !is null); 25 //logf("%s == %s && %s in %s", cur.def.frag.name.value, name, 26 // cur.def.frag.tc.value, typenames 27 // ); 28 if(cur.def.frag.name.value == name 29 && canFind(typenames, cur.def.frag.tc.value)) 30 { 31 //logf("found it"); 32 return cur.def.frag.ss.sel; 33 } else { 34 //logf("not found"); 35 } 36 } 37 cur = cur.follow; 38 } 39 40 //logf("search failed"); 41 return null; 42 } 43 44 unittest { 45 string s = `{ 46 user(id: 1) { 47 friends { 48 name 49 } 50 name 51 age 52 } 53 }`; 54 auto l = Lexer(s); 55 auto p = Parser(l); 56 auto d = p.parseDocument(); 57 58 auto f = findFragment(d, "fooo", ["user"]); 59 assert(f is null); 60 } 61 62 unittest { 63 string s = ` 64 fragment fooo on Hero { 65 name 66 }`; 67 auto l = Lexer(s); 68 auto p = Parser(l); 69 auto d = p.parseDocument(); 70 71 auto f = findFragment(d, "fooo", ["Hero"]); 72 assert(f !is null); 73 assert(f.sel.field.name.name.value == "name"); 74 75 f = findFragment(d, "fooo", ["Villian"]); 76 assert(f is null); 77 } 78 79 struct ArgumentRangeItem { 80 Argument arg; 81 82 @property string name() const pure { 83 return arg.name.value; 84 } 85 86 @property ValueOrVariable value() { 87 return arg.vv; 88 } 89 } 90 91 struct ArgumentRange { 92 ArgumentList cur; 93 94 @property bool empty() const pure { 95 return this.cur is null; 96 } 97 98 @property ArgumentRangeItem front() { 99 return ArgumentRangeItem(this.cur.arg); 100 } 101 102 void popFront() { 103 this.cur = this.cur.follow; 104 } 105 106 @property ArgumentRange save() { 107 return ArgumentRange(this.cur); 108 } 109 } 110 111 struct FieldRangeItem { 112 Field f; 113 Document doc; 114 115 @property string name() { 116 return f.name.name.value; 117 } 118 119 @property string aka() { 120 return f.name.aka.value; 121 } 122 123 ArgumentRange arguments() { 124 if(this.f.args !is null) { 125 return ArgumentRange(f.args.arg); 126 } else { 127 return ArgumentRange(null); 128 } 129 } 130 131 bool hasSelectionSet() pure @safe { 132 return f.ss !is null; 133 } 134 135 FieldRange selectionSet(string[] typenames) { 136 return FieldRange(this.f.ss.sel, this.doc, typenames); 137 } 138 139 } 140 141 struct FieldRange { 142 FixedSizeArray!(Selections,32) cur; 143 Document doc; 144 string[] typenames; 145 146 void test() { 147 import std.format : format; 148 import std.conv : to; 149 import std.algorithm.iteration : map; 150 //logf("%s", this.cur[].map!(i => format("nn %s, ft %s", i !is null, 151 // i !is null ? to!string(i.sel.ruleSelection) : "null")) 152 // ); 153 foreach(it; this.cur[]) { 154 assert(it !is null); 155 assert(it.sel.ruleSelection == SelectionEnum.Field); 156 } 157 } 158 159 this(Selections sels, Document doc, string[] typenames) { 160 this.doc = doc; 161 this.typenames = typenames; 162 this.cur.insertBack(sels); 163 this.build(); 164 this.test(); 165 } 166 167 this(ref FieldRange old) { 168 this.cur = old.cur; 169 this.doc = doc; 170 } 171 172 @property FieldRange save() { 173 return FieldRange(this); 174 } 175 176 @property bool empty() const pure { 177 return this.cur.length == 0; 178 } 179 180 @property FieldRangeItem front() { 181 enforce(!this.cur.empty); 182 enforce(this.cur.back !is null); 183 enforce(this.cur.back.sel !is null); 184 enforce(this.cur.back.sel.field !is null); 185 return FieldRangeItem(this.cur.back.sel.field, this.doc); 186 } 187 188 void popFront() { 189 this.cur.back = this.cur.back.follow; 190 this.build(); 191 this.test(); 192 } 193 194 void build() { 195 if(this.cur.empty) { 196 return; 197 } 198 if(this.cur.back is null) { 199 this.cur.removeBack(); 200 this.build(); 201 this.test(); 202 return; 203 } 204 if(this.cur.back.sel.ruleSelection == SelectionEnum.Field) { 205 return; 206 } else if(this.cur.back.sel.ruleSelection == SelectionEnum.Spread 207 || this.cur.back.sel.ruleSelection == SelectionEnum.IFrag) 208 { 209 Selections follow = this.cur.back.follow; 210 Selections f; 211 if(this.cur.back.sel.ruleSelection == SelectionEnum.Spread) { 212 f = findFragment(doc, 213 this.cur.back.sel.frag.name.value, this.typenames 214 ); 215 } else { 216 f = resolveInlineFragment(this.cur.back.sel.ifrag, 217 this.typenames 218 ); 219 } 220 221 this.cur.removeBack(); 222 223 if(follow !is null) { 224 this.cur.insertBack(follow); 225 this.build(); 226 this.test(); 227 } 228 if(f !is null) { 229 this.cur.insertBack(f); 230 this.build(); 231 this.test(); 232 } 233 } 234 } 235 } 236 237 Selections resolveInlineFragment(InlineFragment ilf, string[] typenames) { 238 import std.algorithm.searching : canFind; 239 final switch(ilf.ruleSelection) { 240 case InlineFragmentEnum.TDS: 241 goto case InlineFragmentEnum.TS; 242 case InlineFragmentEnum.TS: 243 return canFind(typenames, ilf.tc.value) ? ilf.ss.sel : null; 244 case InlineFragmentEnum.DS: 245 return ilf.ss.sel; 246 case InlineFragmentEnum.S: 247 return ilf.ss.sel; 248 } 249 } 250 251 FieldRange fieldRange(OperationDefinition od, Document doc, 252 string[] typenames) 253 { 254 return FieldRange(od.accessNN!(["ss", "sel"]), doc, typenames); 255 } 256 257 FieldRange fieldRange(SelectionSet ss, Document doc, string[] typenames) { 258 return FieldRange(ss.sel, doc, typenames); 259 } 260 261 FieldRangeItem[] fieldRangeArr(Selections sel, Document doc, 262 string[] typenames) 263 { 264 import std.array : array; 265 return FieldRange(sel, doc, typenames).array; 266 } 267 268 struct OpDefRangeItem { 269 Document doc; 270 Definition def; 271 272 FieldRange fieldRange(string[] typenames) { 273 return .fieldRange(accessNN!(["op", "ss"])(this.def), this.doc, 274 typenames 275 ); 276 } 277 } 278 279 struct OpDefRange { 280 Document doc; 281 Definitions defs; 282 283 this(Document doc) { 284 this.doc = doc; 285 this.defs = doc.defs; 286 this.advance(); 287 } 288 289 private void advance() { 290 while(this.defs !is null 291 && this.defs.def.ruleSelection != DefinitionEnum.O) 292 { 293 this.defs = this.defs.follow; 294 } 295 } 296 297 @property bool empty() const { 298 return this.defs is null; 299 } 300 301 @property OpDefRangeItem front() { 302 return OpDefRangeItem(this.doc, this.defs.def); 303 } 304 305 void popFront() { 306 this.defs = this.defs.follow; 307 this.advance(); 308 } 309 310 @property typeof(this) save() { 311 OpDefRange ret; 312 ret.doc = this.doc; 313 ret.defs = this.defs; 314 return ret; 315 } 316 } 317 318 OpDefRange opDefRange(Document doc) { 319 return OpDefRange(doc); 320 } 321 322 unittest { 323 string s = `{ 324 user(id: 1) { 325 friends { 326 name 327 } 328 name 329 age 330 } 331 }`; 332 auto l = Lexer(s); 333 auto p = Parser(l); 334 auto d = p.parseDocument(); 335 336 FieldRange r = fieldRange(d.defs.def.op, d, ["User"]); 337 assert(!r.empty); 338 assert(r.front.name == "user"); 339 ArgumentRange argL = r.front.arguments(); 340 assert(!argL.empty); 341 auto ari = argL.front; 342 assert(ari.name == "id"); 343 argL.popFront(); 344 assert(argL.empty); 345 assert(r.front.hasSelectionSet()); 346 auto fss = r.front.selectionSet(["User"]); 347 assert(!fss.empty); 348 assert(fss.front.name == "friends"); 349 fss.popFront(); 350 assert(!fss.empty); 351 assert(fss.front.name == "name"); 352 fss.popFront(); 353 assert(!fss.empty); 354 assert(fss.front.name == "age"); 355 fss.popFront(); 356 assert(fss.empty); 357 r.popFront(); 358 assert(r.empty); 359 } 360 361 unittest { 362 string s = `{ 363 user(id: 1) { 364 ...foo 365 } 366 } 367 368 fragment foo on User { 369 name 370 age 371 } 372 `; 373 auto l = Lexer(s); 374 auto p = Parser(l); 375 auto d = p.parseDocument(); 376 377 auto f = findFragment(d, "foo", ["User"]); 378 assert(f !is null); 379 380 FieldRange r = fieldRange(d.defs.def.op, d, ["User"]); 381 assert(!r.empty); 382 assert(r.front.name == "user"); 383 ArgumentRange argL = r.front.arguments(); 384 assert(!argL.empty); 385 auto ari = argL.front; 386 assert(ari.name == "id"); 387 argL.popFront(); 388 assert(argL.empty); 389 assert(r.front.hasSelectionSet()); 390 auto fss = r.front.selectionSet(["User"]); 391 assert(!fss.empty); 392 assert(fss.front.name == "name", fss.front.name); 393 fss.popFront(); 394 assert(!fss.empty); 395 assert(fss.front.name == "age"); 396 fss.popFront(); 397 assert(fss.empty); 398 r.popFront(); 399 assert(r.empty); 400 } 401 402 unittest { 403 string s = `{ 404 user(id: 1) { 405 ...foo 406 ...bar 407 } 408 } 409 410 fragment foo on User { 411 name 412 } 413 414 fragment bar on User { 415 age 416 } 417 `; 418 auto l = Lexer(s); 419 auto p = Parser(l); 420 auto d = p.parseDocument(); 421 422 auto f = findFragment(d, "foo", ["User"]); 423 assert(f !is null); 424 425 auto f2 = findFragment(d, "bar", ["User"]); 426 assert(f2 !is null); 427 428 FieldRange r = fieldRange(d.defs.def.op, d, ["User"]); 429 assert(!r.empty); 430 assert(r.front.name == "user"); 431 ArgumentRange argL = r.front.arguments(); 432 assert(!argL.empty); 433 auto ari = argL.front; 434 assert(ari.name == "id"); 435 argL.popFront(); 436 assert(argL.empty); 437 assert(r.front.hasSelectionSet()); 438 auto fss = r.front.selectionSet(["User"]); 439 assert(!fss.empty); 440 assert(fss.front.name == "name", fss.front.name); 441 fss.popFront(); 442 assert(!fss.empty); 443 assert(fss.front.name == "age"); 444 fss.popFront(); 445 assert(fss.empty); 446 r.popFront(); 447 assert(r.empty); 448 } 449 450 unittest { 451 string s = `{ 452 user(id: 1) { 453 ...foo 454 ...bar 455 hello 456 } 457 } 458 459 fragment foo on User { 460 name 461 } 462 463 fragment bar on User { 464 age 465 } 466 `; 467 auto l = Lexer(s); 468 auto p = Parser(l); 469 auto d = p.parseDocument(); 470 471 auto f = findFragment(d, "foo", ["User"]); 472 assert(f !is null); 473 474 auto f2 = findFragment(d, "bar", ["User"]); 475 assert(f2 !is null); 476 477 FieldRange r = fieldRange(d.defs.def.op, d, ["User"]); 478 assert(!r.empty); 479 assert(r.front.name == "user"); 480 ArgumentRange argL = r.front.arguments(); 481 assert(!argL.empty); 482 auto ari = argL.front; 483 assert(ari.name == "id"); 484 argL.popFront(); 485 assert(argL.empty); 486 assert(r.front.hasSelectionSet()); 487 auto fss = r.front.selectionSet(["User"]); 488 assert(!fss.empty); 489 assert(fss.front.name == "name", fss.front.name); 490 fss.popFront(); 491 assert(!fss.empty); 492 assert(fss.front.name == "age"); 493 fss.popFront(); 494 assert(!fss.empty); 495 assert(fss.front.name == "hello"); 496 fss.popFront(); 497 assert(fss.empty); 498 r.popFront(); 499 assert(r.empty); 500 } 501 502 unittest { 503 string s = `{ 504 user(id: 1) { 505 hello 506 ...foo 507 ...bar 508 } 509 } 510 511 fragment foo on User { 512 name 513 } 514 515 fragment bar on User { 516 age 517 } 518 `; 519 auto l = Lexer(s); 520 auto p = Parser(l); 521 auto d = p.parseDocument(); 522 523 auto f = findFragment(d, "foo", ["User"]); 524 assert(f !is null); 525 526 auto f2 = findFragment(d, "bar", ["User"]); 527 assert(f2 !is null); 528 529 FieldRange r = fieldRange(d.defs.def.op, d, ["User"]); 530 assert(!r.empty); 531 assert(r.front.name == "user"); 532 ArgumentRange argL = r.front.arguments(); 533 assert(!argL.empty); 534 auto ari = argL.front; 535 assert(ari.name == "id"); 536 argL.popFront(); 537 assert(argL.empty); 538 assert(r.front.hasSelectionSet()); 539 auto fss = r.front.selectionSet(["User"]); 540 assert(!fss.empty); 541 assert(fss.front.name == "hello", fss.front.name); 542 fss.popFront(); 543 assert(!fss.empty); 544 assert(fss.front.name == "name"); 545 fss.popFront(); 546 assert(!fss.empty); 547 assert(fss.front.name == "age"); 548 fss.popFront(); 549 assert(fss.empty); 550 r.popFront(); 551 assert(r.empty); 552 } 553 554 unittest { 555 string s = `{ 556 user(id: 1) { 557 hello 558 ...foo 559 ...bar 560 } 561 } 562 563 fragment foo on User { 564 name 565 } 566 567 fragment bar on User { 568 age 569 ...baz 570 } 571 572 fragment baz on User { 573 args 574 } 575 `; 576 auto l = Lexer(s); 577 auto p = Parser(l); 578 auto d = p.parseDocument(); 579 580 auto f = findFragment(d, "foo", ["User"]); 581 assert(f !is null); 582 583 auto f2 = findFragment(d, "bar", ["User"]); 584 assert(f2 !is null); 585 586 FieldRange r = fieldRange(d.defs.def.op, d, ["User"]); 587 assert(!r.empty); 588 assert(r.front.name == "user"); 589 ArgumentRange argL = r.front.arguments(); 590 assert(!argL.empty); 591 auto ari = argL.front; 592 assert(ari.name == "id"); 593 argL.popFront(); 594 assert(argL.empty); 595 assert(r.front.hasSelectionSet()); 596 auto fss = r.front.selectionSet(["User"]); 597 assert(!fss.empty); 598 assert(fss.front.name == "hello", fss.front.name); 599 fss.popFront(); 600 assert(!fss.empty); 601 assert(fss.front.name == "name"); 602 fss.popFront(); 603 assert(!fss.empty); 604 assert(fss.front.name == "age"); 605 fss.popFront(); 606 assert(!fss.empty); 607 assert(fss.front.name == "args"); 608 fss.popFront(); 609 assert(fss.empty); 610 r.popFront(); 611 assert(r.empty); 612 } 613 614 unittest { 615 string s = `{ 616 user(id: 1) { 617 hello 618 ...foo 619 zzzz 620 ...bar 621 } 622 } 623 624 fragment foo on User { 625 name 626 } 627 628 fragment bar on User { 629 age 630 ...baz 631 } 632 633 fragment baz on User { 634 args 635 } 636 `; 637 auto l = Lexer(s); 638 auto p = Parser(l); 639 auto d = p.parseDocument(); 640 641 auto f = findFragment(d, "foo", ["User"]); 642 assert(f !is null); 643 644 auto f2 = findFragment(d, "bar", ["User"]); 645 assert(f2 !is null); 646 647 FieldRange r = fieldRange(d.defs.def.op, d, ["User"]); 648 assert(!r.empty); 649 assert(r.front.name == "user"); 650 ArgumentRange argL = r.front.arguments(); 651 assert(!argL.empty); 652 auto ari = argL.front; 653 assert(ari.name == "id"); 654 argL.popFront(); 655 assert(argL.empty); 656 assert(r.front.hasSelectionSet()); 657 auto fss = r.front.selectionSet(["User"]); 658 assert(!fss.empty); 659 assert(fss.front.name == "hello", fss.front.name); 660 fss.popFront(); 661 assert(!fss.empty); 662 assert(fss.front.name == "name"); 663 fss.popFront(); 664 assert(!fss.empty); 665 assert(fss.front.name == "zzzz"); 666 fss.popFront(); 667 assert(!fss.empty); 668 assert(fss.front.name == "age"); 669 fss.popFront(); 670 assert(!fss.empty); 671 assert(fss.front.name == "args"); 672 fss.popFront(); 673 assert(fss.empty); 674 r.popFront(); 675 assert(r.empty); 676 } 677 678 unittest { 679 import std.format : format; 680 import std.stdio; 681 682 string s = `{ 683 user(id: 1) { 684 hello 685 ...foo 686 zzzz 687 ...bar 688 } 689 } 690 691 fragment foo on User { 692 name 693 } 694 695 fragment bar on User { 696 age 697 ...baz 698 } 699 700 fragment baz on User { 701 args 702 } 703 `; 704 auto l = Lexer(s); 705 auto p = Parser(l); 706 auto d = p.parseDocument(); 707 708 auto nn = ["hello", "name", "zzzz", "age", "args"]; 709 size_t cnt = 0; 710 foreach(it; opDefRange(d)) { 711 ++cnt; 712 long idx; 713 foreach(jt; it.fieldRange(["User"])) { 714 //writef("%s(", jt.name); 715 foreach(var; jt.arguments()) { 716 //writef("%s, ", var.name()); 717 } 718 //writeln(")"); 719 foreach(kt; jt.selectionSet(["User"])) { 720 //writeln("\t", kt.name); 721 assert(kt.name == nn[idx], 722 format("%s == %d(%s)", kt.name, idx, nn[idx]) 723 ); 724 ++idx; 725 } 726 } 727 } 728 assert(cnt == 1); 729 }