blob: b0a3918271c061c826dcf033bb47fe1aa4ef5207 [file] [log] [blame]
Anthony F. Molinaroa6530672011-09-18 04:57:50 +00001%%
2%% Licensed to the Apache Software Foundation (ASF) under one
3%% or more contributor license agreements. See the NOTICE file
4%% distributed with this work for additional information
5%% regarding copyright ownership. The ASF licenses this file
6%% to you under the Apache License, Version 2.0 (the
7%% "License"); you may not use this file except in compliance
8%% with the License. You may obtain a copy of the License at
9%%
10%% http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing,
13%% software distributed under the License is distributed on an
14%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15%% KIND, either express or implied. See the License for the
16%% specific language governing permissions and limitations
17%% under the License.
18%%
19%% The JSON protocol implementation was created by
20%% Peter Neumark <neumark.peter@gmail.com> based on
21%% the binary protocol implementation.
22
23-module(thrift_json_protocol).
24
25-behaviour(thrift_protocol).
26
27-include("thrift_constants.hrl").
28-include("thrift_protocol.hrl").
29
Sergei Elin45764092022-09-23 23:21:31 +030030-export([
31 new/1, new/2,
32 read/2,
33 write/2,
34 flush_transport/1,
35 close_transport/1,
36 new_protocol_factory/2
37]).
Anthony F. Molinaroa6530672011-09-18 04:57:50 +000038
39-record(json_context, {
40 % the type of json_context: array or object
Sergei Elin45764092022-09-23 23:21:31 +030041 type :: undefined | array | object,
Anthony F. Molinaroa6530672011-09-18 04:57:50 +000042 % fields read or written
Sergei Elin45764092022-09-23 23:21:31 +030043 fields_processed = 0 :: non_neg_integer()
Anthony F. Molinaroa6530672011-09-18 04:57:50 +000044}).
45
Sergei Elin45764092022-09-23 23:21:31 +030046-type json_context() :: #json_context{}.
47-type jsx_type() :: atom() | {atom(), atom() | string()}.
48-type jsx() :: {event, jsx_type(), [jsx_type()]}.
49
Anthony F. Molinaroa6530672011-09-18 04:57:50 +000050-record(json_protocol, {
Sergei Elin45764092022-09-23 23:21:31 +030051 transport :: term(),
52 context_stack = [] :: [json_context()],
53 jsx :: undefined | jsx()
Anthony F. Molinaroa6530672011-09-18 04:57:50 +000054}).
Anthony F. Molinaroa6530672011-09-18 04:57:50 +000055
56-define(VERSION_1, 1).
57-define(JSON_DOUBLE_PRECISION, 16).
58
59typeid_to_json(?tType_BOOL) -> "tf";
Anthony F. Molinaroa6530672011-09-18 04:57:50 +000060typeid_to_json(?tType_DOUBLE) -> "dbl";
Sergei Elin45764092022-09-23 23:21:31 +030061% NOTE: ?tType_BYTE also match here
Jens Geyer40c28d32015-10-20 23:13:02 +020062typeid_to_json(?tType_I8) -> "i8";
Anthony F. Molinaroa6530672011-09-18 04:57:50 +000063typeid_to_json(?tType_I16) -> "i16";
64typeid_to_json(?tType_I32) -> "i32";
65typeid_to_json(?tType_I64) -> "i64";
66typeid_to_json(?tType_STRING) -> "str";
67typeid_to_json(?tType_STRUCT) -> "rec";
68typeid_to_json(?tType_MAP) -> "map";
69typeid_to_json(?tType_SET) -> "set";
70typeid_to_json(?tType_LIST) -> "lst".
71
72json_to_typeid("tf") -> ?tType_BOOL;
Anthony F. Molinaroa6530672011-09-18 04:57:50 +000073json_to_typeid("dbl") -> ?tType_DOUBLE;
Jens Geyer40c28d32015-10-20 23:13:02 +020074json_to_typeid("i8") -> ?tType_I8;
Anthony F. Molinaroa6530672011-09-18 04:57:50 +000075json_to_typeid("i16") -> ?tType_I16;
76json_to_typeid("i32") -> ?tType_I32;
77json_to_typeid("i64") -> ?tType_I64;
78json_to_typeid("str") -> ?tType_STRING;
79json_to_typeid("rec") -> ?tType_STRUCT;
80json_to_typeid("map") -> ?tType_MAP;
81json_to_typeid("set") -> ?tType_SET;
82json_to_typeid("lst") -> ?tType_LIST.
83
84start_context(object) -> "{";
85start_context(array) -> "[".
86
87end_context(object) -> "}";
88end_context(array) -> "]".
89
Anthony F. Molinaroa6530672011-09-18 04:57:50 +000090new(Transport) ->
91 new(Transport, _Options = []).
92
93new(Transport, _Options) ->
Sergei Elin45764092022-09-23 23:21:31 +030094 State = #json_protocol{transport = Transport},
Anthony F. Molinaroa6530672011-09-18 04:57:50 +000095 thrift_protocol:new(?MODULE, State).
96
97flush_transport(This = #json_protocol{transport = Transport}) ->
98 {NewTransport, Result} = thrift_transport:flush(Transport),
Sergei Elin45764092022-09-23 23:21:31 +030099 {
100 This#json_protocol{
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000101 transport = NewTransport,
102 context_stack = []
Sergei Elin45764092022-09-23 23:21:31 +0300103 },
104 Result
105 }.
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000106
107close_transport(This = #json_protocol{transport = Transport}) ->
108 {NewTransport, Result} = thrift_transport:close(Transport),
Sergei Elin45764092022-09-23 23:21:31 +0300109 {
110 This#json_protocol{
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000111 transport = NewTransport,
112 context_stack = [],
113 jsx = undefined
Sergei Elin45764092022-09-23 23:21:31 +0300114 },
115 Result
116 }.
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000117
118%%%
119%%% instance methods
120%%%
121% places a new context on the stack:
122write(#json_protocol{context_stack = Stack} = State0, {enter_context, Type}) ->
123 {State1, ok} = write_values(State0, [{context_pre_item, false}]),
Sergei Elin45764092022-09-23 23:21:31 +0300124 State2 = State1#json_protocol{
125 context_stack = [
126 #json_context{type = Type} | Stack
127 ]
128 },
129 write_values(State2, [list_to_binary(start_context(Type))]);
130% removes the topmost context from stack
131write(#json_protocol{context_stack = [CurrCtxt | Stack]} = State0, {exit_context}) ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000132 Type = CurrCtxt#json_context.type,
133 State1 = State0#json_protocol{context_stack = Stack},
134 write_values(State1, [
Sergei Elin45764092022-09-23 23:21:31 +0300135 list_to_binary(end_context(Type)),
136 {context_post_item, false}
137 ]);
138% writes necessary prelude to field or container depending on current context
139write(
140 #json_protocol{context_stack = []} = This0,
141 {context_pre_item, _}
142) ->
143 {This0, ok};
144write(
145 #json_protocol{context_stack = [Context | _CtxtTail]} = This0,
146 {context_pre_item, MayNeedQuotes}
147) ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000148 FieldNo = Context#json_context.fields_processed,
149 CtxtType = Context#json_context.type,
150 Rem = FieldNo rem 2,
151 case {CtxtType, FieldNo, Rem, MayNeedQuotes} of
Sergei Elin45764092022-09-23 23:21:31 +0300152 % array element (not first)
153 {array, N, _, _} when N > 0 ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000154 write(This0, <<",">>);
Sergei Elin45764092022-09-23 23:21:31 +0300155 % non-string object key (first)
156 {object, 0, _, true} ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000157 write(This0, <<"\"">>);
Sergei Elin45764092022-09-23 23:21:31 +0300158 % non-string object key (not first)
159 {object, N, 0, true} when N > 0 ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000160 write(This0, <<",\"">>);
Sergei Elin45764092022-09-23 23:21:31 +0300161 % string object key (not first)
162 {object, N, 0, false} when N > 0 ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000163 write(This0, <<",">>);
Sergei Elin45764092022-09-23 23:21:31 +0300164 % no pre-field necessary
165 _ ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000166 {This0, ok}
167 end;
Sergei Elin45764092022-09-23 23:21:31 +0300168% writes necessary postlude to field or container depending on current context
169write(
170 #json_protocol{context_stack = []} = This0,
171 {context_post_item, _}
172) ->
173 {This0, ok};
174write(
175 #json_protocol{context_stack = [Context | CtxtTail]} = This0,
176 {context_post_item, MayNeedQuotes}
177) ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000178 FieldNo = Context#json_context.fields_processed,
179 CtxtType = Context#json_context.type,
180 Rem = FieldNo rem 2,
Sergei Elin45764092022-09-23 23:21:31 +0300181 {This1, ok} =
182 case {CtxtType, Rem, MayNeedQuotes} of
183 % non-string object key
184 {object, 0, true} ->
185 write(This0, <<"\":">>);
186 % string object key
187 {object, 0, false} ->
188 write(This0, <<":">>);
189 % no pre-field necessary
190 _ ->
191 {This0, ok}
192 end,
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000193 NewContext = Context#json_context{fields_processed = FieldNo + 1},
Sergei Elin45764092022-09-23 23:21:31 +0300194 {This1#json_protocol{context_stack = [NewContext | CtxtTail]}, ok};
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000195write(This0, #protocol_message_begin{
196 name = Name,
197 type = Type,
Sergei Elin45764092022-09-23 23:21:31 +0300198 seqid = Seqid
199}) ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000200 write_values(This0, [
201 {enter_context, array},
202 {i32, ?VERSION_1},
203 {string, Name},
204 {i32, Type},
205 {i32, Seqid}
206 ]);
Sergei Elin45764092022-09-23 23:21:31 +0300207write(This, message_end) ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000208 write_values(This, [{exit_context}]);
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000209% Example field expression: "1":{"dbl":3.14}
210write(This0, #protocol_field_begin{
Sergei Elin45764092022-09-23 23:21:31 +0300211 name = _Name,
212 type = Type,
213 id = Id
214}) ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000215 write_values(This0, [
216 % entering 'outer' object
217 {i16, Id},
218 % entering 'outer' object
219 {enter_context, object},
220 {string, typeid_to_json(Type)}
221 ]);
Sergei Elin45764092022-09-23 23:21:31 +0300222write(This, field_stop) ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000223 {This, ok};
Sergei Elin45764092022-09-23 23:21:31 +0300224write(This, field_end) ->
225 write_values(This, [{exit_context}]);
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000226% Example message with map: [1,"testMap",1,0,{"1":{"map":["i32","i32",3,{"7":77,"8":88,"9":99}]}}]
227write(This0, #protocol_map_begin{
Sergei Elin45764092022-09-23 23:21:31 +0300228 ktype = Ktype,
229 vtype = Vtype,
230 size = Size
231}) ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000232 write_values(This0, [
233 {enter_context, array},
234 {string, typeid_to_json(Ktype)},
235 {string, typeid_to_json(Vtype)},
236 {i32, Size},
237 {enter_context, object}
238 ]);
Sergei Elin45764092022-09-23 23:21:31 +0300239write(This, map_end) ->
240 write_values(This, [
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000241 {exit_context},
242 {exit_context}
243 ]);
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000244write(This0, #protocol_list_begin{
Sergei Elin45764092022-09-23 23:21:31 +0300245 etype = Etype,
246 size = Size
247}) ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000248 write_values(This0, [
249 {enter_context, array},
250 {string, typeid_to_json(Etype)},
251 {i32, Size}
252 ]);
Sergei Elin45764092022-09-23 23:21:31 +0300253write(This, list_end) ->
254 write_values(This, [
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000255 {exit_context}
256 ]);
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000257% example message with set: [1,"testSet",1,0,{"1":{"set":["i32",3,1,2,3]}}]
258write(This0, #protocol_set_begin{
Sergei Elin45764092022-09-23 23:21:31 +0300259 etype = Etype,
260 size = Size
261}) ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000262 write_values(This0, [
263 {enter_context, array},
264 {string, typeid_to_json(Etype)},
265 {i32, Size}
266 ]);
Sergei Elin45764092022-09-23 23:21:31 +0300267write(This, set_end) ->
268 write_values(This, [
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000269 {exit_context}
270 ]);
271% example message with struct: [1,"testStruct",1,0,{"1":{"rec":{"1":{"str":"worked"},"4":{"i8":1},"9":{"i32":1073741824},"11":{"i64":1152921504606847000}}}}]
Sergei Elin45764092022-09-23 23:21:31 +0300272write(This, #protocol_struct_begin{}) ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000273 write_values(This, [
274 {enter_context, object}
275 ]);
Sergei Elin45764092022-09-23 23:21:31 +0300276write(This, struct_end) ->
277 write_values(This, [
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000278 {exit_context}
279 ]);
Sergei Elin45764092022-09-23 23:21:31 +0300280write(This, {bool, true}) ->
281 write_values(This, [
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000282 {context_pre_item, true},
283 <<"true">>,
284 {context_post_item, true}
285 ]);
Sergei Elin45764092022-09-23 23:21:31 +0300286write(This, {bool, false}) ->
287 write_values(This, [
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000288 {context_pre_item, true},
289 <<"false">>,
290 {context_post_item, true}
291 ]);
Sergei Elin45764092022-09-23 23:21:31 +0300292write(This, {byte, Byte}) ->
293 write_values(This, [
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000294 {context_pre_item, true},
295 list_to_binary(integer_to_list(Byte)),
296 {context_post_item, true}
297 ]);
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000298write(This, {i16, I16}) ->
299 write(This, {byte, I16});
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000300write(This, {i32, I32}) ->
301 write(This, {byte, I32});
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000302write(This, {i64, I64}) ->
303 write(This, {byte, I64});
Sergei Elin45764092022-09-23 23:21:31 +0300304write(This, {double, Double}) ->
305 write_values(This, [
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000306 {context_pre_item, true},
Sergei Elin45764092022-09-23 23:21:31 +0300307 list_to_binary(io_lib:format("~.*f", [?JSON_DOUBLE_PRECISION, Double])),
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000308 {context_post_item, true}
309 ]);
Sergei Elin45764092022-09-23 23:21:31 +0300310write(This0, {string, Str}) ->
311 write_values(This0, [
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000312 {context_pre_item, false},
313 case is_binary(Str) of
314 true -> Str;
alisdair sullivancb3f3f32014-07-14 21:50:43 -0700315 false -> <<"\"", (list_to_binary(Str))/binary, "\"">>
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000316 end,
317 {context_post_item, false}
318 ]);
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000319%% TODO: binary fields should be base64 encoded?
320
321%% Data :: iolist()
322write(This = #json_protocol{transport = Trans}, Data) ->
323 %io:format("Data ~p Ctxt ~p~n~n", [Data, This#json_protocol.context_stack]),
324 {NewTransport, Result} = thrift_transport:write(Trans, Data),
325 {This#json_protocol{transport = NewTransport}, Result}.
326
327write_values(This0, ValueList) ->
328 FinalState = lists:foldl(
329 fun(Val, ThisIn) ->
330 {ThisOut, ok} = write(ThisIn, Val),
331 ThisOut
332 end,
333 This0,
Sergei Elin45764092022-09-23 23:21:31 +0300334 ValueList
335 ),
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000336 {FinalState, ok}.
337
Sergei Elin45764092022-09-23 23:21:31 +0300338%% I wish the erlang version of the transport interface included a
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000339%% read_all function (like eg. the java implementation). Since it doesn't,
340%% here's my version (even though it probably shouldn't be in this file).
341%%
342%% The resulting binary is immediately send to the JSX stream parser.
343%% Subsequent calls to read actually operate on the events returned by JSX.
344read_all(#json_protocol{transport = Transport0} = State) ->
345 {Transport1, Bin} = read_all_1(Transport0, []),
alisdair sullivancb3f3f32014-07-14 21:50:43 -0700346 P = thrift_json_parser:parser(),
Sergei Elin45764092022-09-23 23:21:31 +0300347 [First | Rest] = P(Bin),
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000348 State#json_protocol{
349 transport = Transport1,
alisdair sullivancb3f3f32014-07-14 21:50:43 -0700350 jsx = {event, First, Rest}
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000351 }.
352
353read_all_1(Transport0, IoList) ->
354 {Transport1, Result} = thrift_transport:read(Transport0, 1),
355 case Result of
Sergei Elin45764092022-09-23 23:21:31 +0300356 % nothing read: assume we're done
357 {ok, <<>>} ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000358 {Transport1, iolist_to_binary(lists:reverse(IoList))};
Sergei Elin45764092022-09-23 23:21:31 +0300359 % character successfully read; read more
360 {ok, Data} ->
361 read_all_1(Transport1, [Data | IoList]);
362 % we're done
363 {error, 'EOF'} ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000364 {Transport1, iolist_to_binary(lists:reverse(IoList))}
365 end.
366
367% Expect reads an event from the JSX event stream. It receives an event or data
368% type as input. Comparing the read event from the one is was passed, it
369% returns an error if something other than the expected value is encountered.
370% Expect also maintains the context stack in #json_protocol.
Sergei Elin45764092022-09-23 23:21:31 +0300371expect(#json_protocol{jsx = {event, {Type, Data} = Ev, [Next | Rest]}} = State, ExpectedType) ->
372 NextState = State#json_protocol{jsx = {event, Next, Rest}},
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000373 case Type == ExpectedType of
Sergei Elin45764092022-09-23 23:21:31 +0300374 true ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000375 {NextState, {ok, convert_data(Type, Data)}};
376 false ->
377 {NextState, {error, {unexpected_json_event, Ev}}}
378 end;
Sergei Elin45764092022-09-23 23:21:31 +0300379expect(#json_protocol{jsx = {event, Event, Next}} = State, ExpectedEvent) ->
380 expect(State#json_protocol{jsx = {event, {Event, none}, Next}}, ExpectedEvent).
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000381
382convert_data(integer, I) -> list_to_integer(I);
383convert_data(float, F) -> list_to_float(F);
384convert_data(_, D) -> D.
385
386expect_many(State, ExpectedList) ->
387 expect_many_1(State, ExpectedList, [], ok).
388
389expect_many_1(State, [], ResultList, Status) ->
390 {State, {Status, lists:reverse(ResultList)}};
Sergei Elin45764092022-09-23 23:21:31 +0300391expect_many_1(State, [Expected | ExpTail], ResultList, _PrevStatus) ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000392 {State1, {Status, Data}} = expect(State, Expected),
Sergei Elin45764092022-09-23 23:21:31 +0300393 NewResultList = [Data | ResultList],
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000394 case Status of
395 % in case of error, end prematurely
396 error -> expect_many_1(State1, [], NewResultList, Status);
397 ok -> expect_many_1(State1, ExpTail, NewResultList, Status)
398 end.
399
400% wrapper around expect to make life easier for container opening/closing functions
401expect_nodata(This, ExpectedList) ->
402 case expect_many(This, ExpectedList) of
Sergei Elin45764092022-09-23 23:21:31 +0300403 {State, {ok, _}} ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000404 {State, ok};
Sergei Elin45764092022-09-23 23:21:31 +0300405 Error ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000406 Error
407 end.
408
Sergei Elin45764092022-09-23 23:21:31 +0300409read_field(#json_protocol{jsx = {event, Field, [Next | Rest]}} = State) ->
410 NewState = State#json_protocol{jsx = {event, Next, Rest}},
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000411 {NewState, Field}.
412
413read(This0, message_begin) ->
414 % call read_all to get the contents of the transport buffer into JSX.
415 This1 = read_all(This0),
Sergei Elin45764092022-09-23 23:21:31 +0300416 case
417 expect_many(
418 This1,
419 [start_array, integer, string, integer, integer]
420 )
421 of
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000422 {This2, {ok, [_, Version, Name, Type, SeqId]}} ->
423 case Version =:= ?VERSION_1 of
424 true ->
Sergei Elin45764092022-09-23 23:21:31 +0300425 {This2, #protocol_message_begin{
426 name = Name,
427 type = Type,
428 seqid = SeqId
429 }};
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000430 false ->
431 {This2, {error, no_json_protocol_version}}
432 end;
Sergei Elin45764092022-09-23 23:21:31 +0300433 Other ->
434 Other
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000435 end;
Sergei Elin45764092022-09-23 23:21:31 +0300436read(This, message_end) ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000437 expect_nodata(This, [end_array]);
Sergei Elin45764092022-09-23 23:21:31 +0300438read(This, struct_begin) ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000439 expect_nodata(This, [start_object]);
Sergei Elin45764092022-09-23 23:21:31 +0300440read(This, struct_end) ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000441 expect_nodata(This, [end_object]);
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000442read(This0, field_begin) ->
Sergei Elin45764092022-09-23 23:21:31 +0300443 {This1, Read} = expect_many(
444 This0,
445 %field id
446 [
447 key,
448 % {} surrounding field
449 start_object,
450 % type of field
451 key
452 ]
453 ),
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000454 case Read of
455 {ok, [FieldIdStr, _, FieldType]} ->
456 {This1, #protocol_field_begin{
Sergei Elin45764092022-09-23 23:21:31 +0300457 type = json_to_typeid(FieldType),
458 % TODO: do we need to wrap this in a try/catch?
459 id = list_to_integer(FieldIdStr)
460 }};
461 {error, [{unexpected_json_event, {end_object, none}}]} ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000462 {This1, #protocol_field_begin{type = ?tType_STOP}};
Sergei Elin45764092022-09-23 23:21:31 +0300463 Other ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000464 io:format("**** OTHER branch selected ****"),
465 {This1, Other}
466 end;
Sergei Elin45764092022-09-23 23:21:31 +0300467read(This, field_end) ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000468 expect_nodata(This, [end_object]);
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000469% Example message with map: [1,"testMap",1,0,{"1":{"map":["i32","i32",3,{"7":77,"8":88,"9":99}]}}]
470read(This0, map_begin) ->
Sergei Elin45764092022-09-23 23:21:31 +0300471 case
472 expect_many(
473 This0,
474 [
475 start_array,
476 % key type
477 string,
478 % value type
479 string,
480 % size
481 integer,
482 % the following object contains the map
483 start_object
484 ]
485 )
486 of
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000487 {This1, {ok, [_, Ktype, Vtype, Size, _]}} ->
Sergei Elin45764092022-09-23 23:21:31 +0300488 {This1, #protocol_map_begin{
489 ktype = Ktype,
490 vtype = Vtype,
491 size = Size
492 }};
493 Other ->
494 Other
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000495 end;
Sergei Elin45764092022-09-23 23:21:31 +0300496read(This, map_end) ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000497 expect_nodata(This, [end_object, end_array]);
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000498read(This0, list_begin) ->
Sergei Elin45764092022-09-23 23:21:31 +0300499 case
500 expect_many(
501 This0,
502 [
503 start_array,
504 % element type
505 string,
506 % size
507 integer
508 ]
509 )
510 of
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000511 {This1, {ok, [_, Etype, Size]}} ->
512 {This1, #protocol_list_begin{
513 etype = Etype,
Sergei Elin45764092022-09-23 23:21:31 +0300514 size = Size
515 }};
516 Other ->
517 Other
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000518 end;
Sergei Elin45764092022-09-23 23:21:31 +0300519read(This, list_end) ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000520 expect_nodata(This, [end_array]);
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000521% example message with set: [1,"testSet",1,0,{"1":{"set":["i32",3,1,2,3]}}]
522read(This0, set_begin) ->
Sergei Elin45764092022-09-23 23:21:31 +0300523 case
524 expect_many(
525 This0,
526 [
527 start_array,
528 % element type
529 string,
530 % size
531 integer
532 ]
533 )
534 of
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000535 {This1, {ok, [_, Etype, Size]}} ->
536 {This1, #protocol_set_begin{
537 etype = Etype,
Sergei Elin45764092022-09-23 23:21:31 +0300538 size = Size
539 }};
540 Other ->
541 Other
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000542 end;
Sergei Elin45764092022-09-23 23:21:31 +0300543read(This, set_end) ->
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000544 expect_nodata(This, [end_array]);
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000545read(This0, field_stop) ->
546 {This0, ok};
547%%
548
549read(This0, bool) ->
550 {This1, Field} = read_field(This0),
Sergei Elin45764092022-09-23 23:21:31 +0300551 Value =
552 case Field of
553 {literal, I} ->
554 {ok, I};
555 _Other ->
556 {error, unexpected_event_for_boolean}
557 end,
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000558 {This1, Value};
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000559read(This0, byte) ->
560 {This1, Field} = read_field(This0),
Sergei Elin45764092022-09-23 23:21:31 +0300561 Value =
562 case Field of
563 {key, K} ->
564 {ok, list_to_integer(K)};
565 {integer, I} ->
566 {ok, list_to_integer(I)};
567 _Other ->
568 {error, unexpected_event_for_integer}
569 end,
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000570 {This1, Value};
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000571read(This0, i16) ->
572 read(This0, byte);
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000573read(This0, i32) ->
574 read(This0, byte);
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000575read(This0, i64) ->
576 read(This0, byte);
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000577read(This0, double) ->
578 {This1, Field} = read_field(This0),
Sergei Elin45764092022-09-23 23:21:31 +0300579 Value =
580 case Field of
581 {float, I} ->
582 {ok, list_to_float(I)};
583 _Other ->
584 {error, unexpected_event_for_double}
585 end,
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000586 {This1, Value};
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000587% returns a binary directly, call binary_to_list if necessary
588read(This0, string) ->
589 {This1, Field} = read_field(This0),
Sergei Elin45764092022-09-23 23:21:31 +0300590 Value =
591 case Field of
592 {string, I} ->
593 {ok, I};
594 {key, J} ->
595 {ok, J};
596 _Other ->
597 {error, unexpected_event_for_string}
598 end,
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000599 {This1, Value}.
600
601%%%% FACTORY GENERATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
602
603%% returns a (fun() -> thrift_protocol())
604new_protocol_factory(TransportFactory, _Options) ->
605 % Only strice read/write are implemented
606 F = fun() ->
Sergei Elin45764092022-09-23 23:21:31 +0300607 {ok, Transport} = TransportFactory(),
608 thrift_json_protocol:new(Transport, [])
609 end,
Anthony F. Molinaroa6530672011-09-18 04:57:50 +0000610 {ok, F}.