1 /**
2  * This module takes a `std.json` JSONValue and generates a `stdx.data.json` token stream.
3  * It provides Phobos backwards compatibility for `std_data_json`.
4  */
5 module text.json.JsonValueRange;
6 
7 import funkwerk.stdx.data.json.lexer;
8 import funkwerk.stdx.data.json.parser;
9 import std.algorithm : count;
10 import std.json;
11 import std.range : drop;
12 
13 static assert(isJSONParserNodeInputRange!JsonValueRange);
14 
15 struct JsonValueRange
16 {
17     public bool empty;
18 
19     private JSONParserNode!string currentValue;
20 
21     private ValueIterator[] iterators;
22 
23     private int level;
24 
25     invariant(this.level < cast(int) this.iterators.length);
26 
27     public this(JSONValue value)
28     {
29         this.empty = false;
30         this.level = -1;
31         stepInto(value);
32     }
33 
34     public @property ref JSONParserNode!string front()
35     in (!empty)
36     {
37         return this.currentValue;
38     }
39 
40     private void stepInto(JSONValue value)
41     {
42         alias Token = JSONToken!string;
43 
44         with (JSONType) final switch (value.type)
45         {
46             case null_:
47                 this.currentValue.literal = Token(null);
48                 break;
49             case string:
50                 this.currentValue.literal = Token(value.str);
51                 break;
52             case integer:
53                 this.currentValue.literal = Token(value.integer);
54                 break;
55             case uinteger:
56                 this.currentValue.literal = Token(value.uinteger);
57                 break;
58             case float_:
59                 this.currentValue.literal = Token(value.floating);
60                 break;
61             case array:
62                 this.currentValue.kind = JSONParserNodeKind.arrayStart;
63                 pushState(value);
64                 break;
65             case object:
66                 this.currentValue.kind = JSONParserNodeKind.objectStart;
67                 pushState(value);
68                 break;
69             case true_:
70                 this.currentValue.literal = Token(true);
71                 break;
72             case false_:
73                 this.currentValue.literal = Token(false);
74                 break;
75         }
76     }
77 
78     private void pushState(JSONValue value)
79     {
80         if (this.level + 1 == this.iterators.length)
81         {
82             this.iterators ~= ValueIterator(value);
83         }
84         else
85         {
86             this.iterators[this.level + 1] = ValueIterator(value);
87         }
88         this.level++;
89     }
90 
91     public void popFront()
92     in (!empty)
93     {
94         if (outOfValues)
95         {
96             empty = true;
97             return;
98         }
99 
100         if (current.value.type == JSONType.object)
101         {
102             if (current.nextIndex == current.value.objectNoRef.length)
103             {
104                 this.currentValue.kind = JSONParserNodeKind.objectEnd;
105                 popState;
106                 return;
107             }
108 
109             if (!current.usedKey)
110             {
111                 this.currentValue.key = current.value.objectNoRef.byKeyValue.drop(current.nextIndex).front.key;
112                 current.usedKey = true;
113                 return;
114             }
115 
116             auto value = current.value.objectNoRef.byKeyValue.drop(current.nextIndex).front.value;
117 
118             current.usedKey = false;
119             current.nextIndex++;
120             stepInto(value);
121             return;
122         }
123         else if (current.value.type == JSONType.array)
124         {
125             if (current.nextIndex == current.value.arrayNoRef.length)
126             {
127                 this.currentValue.kind = JSONParserNodeKind.arrayEnd;
128                 popState;
129                 return;
130             }
131             stepInto(current.value.arrayNoRef[current.nextIndex++]);
132         }
133         else
134         {
135             assert(false, "unexpected value type");
136         }
137     }
138 
139     private void popState()
140     {
141         this.level--;
142     }
143 
144     private ref ValueIterator current()
145     in (!outOfValues)
146     {
147         return this.iterators[this.level];
148     }
149 
150     public bool outOfValues() const
151     {
152         return this.level == -1;
153     }
154 }
155 
156 private struct ValueIterator
157 {
158     JSONValue value;
159 
160     invariant(value.type == JSONType.array || value.type == JSONType.object);
161 
162     // if `value` is an array or object, indicates the next element to be selected.
163     size_t nextIndex = 0;
164 
165     bool usedKey; // objects are key, value, key, value => false, true, false, true...
166 }