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