1 module graphql.argumentextractor;
2 
3 import std.array : back, empty, popBack;
4 import std.conv : to;
5 import std.format : format;
6 import std.exception : enforce;
7 
8 import vibe.data.json;
9 
10 import graphql.visitor;
11 import graphql.ast;
12 
13 @safe:
14 
15 class ArgumentExtractor : Visitor {
16 	alias enter = Visitor.enter;
17 	alias exit = Visitor.exit;
18 	alias accept = Visitor.accept;
19 
20 	Json arguments;
21 	Json variables;
22 
23 	string[] curNames;
24 
25 	this(Json variables) {
26 		this.variables = variables;
27 		this.arguments = Json.emptyObject();
28 	}
29 
30 	void assign(Json toAssign) @trusted {
31 		Json* arg = &this.arguments;
32 		//logf("%(%s.%) %s %s", this.curNames, this.arguments, toAssign);
33 		assert(!this.curNames.empty);
34 		foreach(idx; 0 .. this.curNames.length - 1) {
35 			enforce(arg !is null);
36 			arg = &((*arg)[this.curNames[idx]]);
37 		}
38 
39 		enforce(arg !is null);
40 
41 		if(this.curNames.back in (*arg)
42 				&& ((*arg)[this.curNames.back]).type == Json.Type.array)
43 		{
44 			((*arg)[this.curNames.back]) ~= toAssign;
45 		} else if((*arg).type == Json.Type.object) {
46 			((*arg)[this.curNames.back]) = toAssign;
47 		} else {
48 			((*arg)[this.curNames.back]) = toAssign;
49 		}
50 	}
51 
52 	override void enter(const(Argument) arg) {
53 		this.curNames ~= arg.name.value;
54 	}
55 
56 	override void exit(const(Argument) arg) {
57 		this.curNames.popBack();
58 	}
59 
60 	override void accept(const(ObjectValues) obj) {
61 		enter(obj);
62 		final switch(obj.ruleSelection) {
63 			case ObjectValuesEnum.V:
64 				this.curNames ~= obj.name.value;
65 				obj.val.visit(this);
66 				this.curNames.popBack();
67 				break;
68 			case ObjectValuesEnum.Vsc:
69 				this.curNames ~= obj.name.value;
70 				obj.val.visit(this);
71 				this.curNames.popBack();
72 				obj.follow.visit(this);
73 				break;
74 			case ObjectValuesEnum.Vs:
75 				this.curNames ~= obj.name.value;
76 				obj.val.visit(this);
77 				this.curNames.popBack();
78 				obj.follow.visit(this);
79 				break;
80 		}
81 		exit(obj);
82 	}
83 
84 	override void enter(const(Variable) var) {
85 		string varName = var.name.value;
86 		enforce(varName in this.variables,
87 				format("Variable with name %s required", varName)
88 			);
89 		this.assign(this.variables[varName]);
90 	}
91 
92 	override void enter(const(Value) val) {
93 		final switch(val.ruleSelection) {
94 			case ValueEnum.STR:
95 				this.assign(Json(val.tok.value));
96 				break;
97 			case ValueEnum.INT:
98 				this.assign(Json(to!long(val.tok.value)));
99 				break;
100 			case ValueEnum.FLOAT:
101 				this.assign(Json(to!double(val.tok.value)));
102 				break;
103 			case ValueEnum.T:
104 				this.assign(Json(true));
105 				break;
106 			case ValueEnum.F:
107 				this.assign(Json(false));
108 				break;
109 			case ValueEnum.ARR:
110 				this.assign(Json.emptyArray());
111 				break;
112 			case ValueEnum.O:
113 				this.assign(Json.emptyObject());
114 				break;
115 			case ValueEnum.E:
116 				this.assign(Json(val.tok.value));
117 				break;
118 		}
119 	}
120 }
121