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