Skip to content

Commit 26ac9ac

Browse files
committed
Load .avdl to erlavro internal format
Now it's possible to use basic .avdl to encode/decode avro
1 parent d011692 commit 26ac9ac

File tree

7 files changed

+62
-19
lines changed

7 files changed

+62
-19
lines changed

src/avro.erl

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ set_json_provider(Module) -> avro_json_compat:set_provider(Module).
154154
get_json_provider() -> avro_json_compat:get_provider().
155155

156156
%% @doc Decode JSON format avro schema into `erlavro' internals.
157-
-spec decode_schema(binary()) -> avro_type().
157+
%% @param JSON: JSON binary or erlang `map()' json representation
158+
-spec decode_schema(binary() | map() | [map()]) -> avro_type().
158159
decode_schema(JSON) -> avro_json_decoder:decode_schema(JSON).
159160

160161
%% @doc Make type lookup function from type definition.
@@ -187,7 +188,8 @@ make_lkup_fun(AssignedName, Type) ->
187188
%% * allow_type_redefine: `boolean()'
188189
%% This option is to allow one type being defined more than once.
189190
%% @end
190-
-spec decode_schema(binary(), proplists:proplist()) -> avro_type().
191+
-spec decode_schema(binary() | map() | [map()], proplists:proplist()) ->
192+
avro_type().
191193
decode_schema(JSON, Options) ->
192194
avro_json_decoder:decode_schema(JSON, Options).
193195

@@ -280,7 +282,8 @@ make_decoder(Schema, Options) ->
280282
%% takes only one `binary()' input arg.
281283
-spec make_simple_decoder(avro_type() | binary(), codec_options()) ->
282284
simple_decoder().
283-
make_simple_decoder(JSON, Options) when is_binary(JSON) ->
285+
make_simple_decoder(JSON, Options) when is_binary(JSON);
286+
is_map(JSON) ->
284287
make_simple_decoder(decode_schema(JSON), Options);
285288
make_simple_decoder(Type, Options) when ?IS_TYPE_RECORD(Type) ->
286289
Lkup = make_lkup_fun(Type),

src/avro_idl.erl

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
%%% as create Avro encoders and decoders.
55
%%% @end
66
%%% @reference See [https://avro.apache.org/docs/current/idl.html]
7-
%%% @author Sergey Prokhhorov <me@seriyps.ru>
7+
%%% @author Sergey Prokhorov <me@seriyps.ru>
88
-module(avro_idl).
99

10+
-export([decode_schema/2]).
1011
-export([new_context/1,
1112
str_to_avpr/2,
1213
protocol_to_avpr/2,
@@ -16,6 +17,23 @@
1617

1718
-record(st, {cwd}).
1819

20+
decode_schema(SchemaStr, Cwd) ->
21+
Protocol = str_to_avpr(SchemaStr, Cwd),
22+
#{<<"types">> := Types0} = Protocol,
23+
Types1 = lists:filter(
24+
fun(#{<<"type">> := TName}) ->TName =/= <<"error">> end, Types0),
25+
Ns = maps:get(<<"namespace">>, Protocol, ?AVRO_NS_GLOBAL),
26+
Types = lists:map(
27+
fun(T) ->
28+
case maps:is_key(<<"namespace">>, T) of
29+
false ->
30+
T#{<<"namespace">> => Ns};
31+
true ->
32+
T
33+
end
34+
end, Types1),
35+
avro:decode_schema(Types, [{ignore_bad_default_values, true}]).
36+
1937
new_context(Cwd) ->
2038
#st{cwd = Cwd}.
2139

@@ -64,7 +82,7 @@ typedecl_to_avsc(#enum{name = Name, meta = Meta, variants = Vars}, _St) ->
6482
meta(
6583
#{<<"type">> => ?AVRO_ENUM,
6684
<<"name">> => b(Name),
67-
<<"variants">> => lists:map(fun b/1, Vars)
85+
<<"symbols">> => lists:map(fun b/1, Vars)
6886
},
6987
Meta);
7088
typedecl_to_avsc(#fixed{name = Name, meta = Meta, size = Size}, _St) ->

src/avro_idl_lexer.xrl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
%% @doc Avro IDL lexer
22
%% @end
33
%% @reference See [https://avro.apache.org/docs/current/idl.html]
4-
%% @author Sergey Prokhhorov <me@seriyps.ru>
4+
%% @author Sergey Prokhorov <me@seriyps.ru>
55

66
Definitions.
77

src/avro_idl_parser.yrl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Header "%%% @doc Avro IDL parser
33
%%% XXX: all `comment_v' tokens should be filtered-out before parsing!
44
%%% @end
55
%%% @reference See [https://avro.apache.org/docs/current/idl.html]
6-
%%% @author Sergey Prokhhorov <me@seriyps.ru>".
6+
%%% @author Sergey Prokhorov <me@seriyps.ru>".
77

88
Terminals id ns_id null string_v doc_v float_v integer_v bool_v annotation_v
99
primitive_t logical_t decimal_t
@@ -120,7 +120,7 @@ import_file_type -> schema_k : schema.
120120

121121
%% -- Enum typedef
122122
enum ->
123-
enum_t id '{' id enum_variants :
123+
enum_t id '{' id enum_variants : % TODO: add support for default
124124
#enum{name = value_of('$2'), variants = [value_of('$4') | '$5']}.
125125
enum ->
126126
meta enum :

src/avro_json_decoder.erl

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
%%%_* APIs =====================================================================
4848

4949
%% @doc Decode JSON format avro schema into erlavro internals.
50-
-spec decode_schema(binary()) -> avro_type().
50+
-spec decode_schema(binary() | map() | [map()]) -> avro_type().
5151
decode_schema(JSON) ->
5252
decode_schema(JSON, _Opts = []).
5353

@@ -66,7 +66,7 @@ decode_schema(JSON) ->
6666
%% * allow_type_redefine: `boolean()'
6767
%% This option is to allow one type being defined more than once.
6868
%% @end
69-
-spec decode_schema(binary(), sc_opts()) -> avro_type().
69+
-spec decode_schema(binary() | map() | [map()], sc_opts()) -> avro_type().
7070
decode_schema(JSON, Opts) when is_list(Opts) ->
7171
%% Parse JSON first
7272
Type = parse_schema(decode_json(JSON)),
@@ -513,9 +513,21 @@ do_parse_union_ex(ValueTypeName, Value, UnionType,
513513
%% 'map' is a better option, but we have to keep it backward compatible.
514514
%% 'proplist' is not an option because otherwise there is no way to tell
515515
%% apart 'object' and 'array'.
516-
-spec decode_json(binary()) -> json_value().
516+
-spec decode_json(binary() | map() | [map()]) -> json_value().
517+
decode_json(Parsed) when is_map(Parsed);
518+
is_list(Parsed) ->
519+
map_to_tuple(Parsed);
517520
decode_json(JSON) -> avro_json_compat:decode(JSON, [{object_format, tuple}]).
518521

522+
%% recursively convert map to json-tuple format
523+
map_to_tuple(Map) when is_map(Map) ->
524+
{[{K, map_to_tuple(V)}
525+
|| {K, V} <- maps:to_list(Map)]};
526+
map_to_tuple(Array) when is_list(Array) ->
527+
lists:map(fun map_to_tuple/1, Array);
528+
map_to_tuple(Other) ->
529+
Other.
530+
519531
%% Filter out non-custom properties.
520532
-spec filter_custom_props([{binary(), json_value()}], [name()]) ->
521533
[custom_prop()].

test/avro_idl_parse_tests.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
%% @doc Tests for IDL lexer + parser
22
%% @end
3-
%% @author Sergey Prokhhorov <me@seriyps.ru>
3+
%% @author Sergey Prokhorov <me@seriyps.ru>
44
-module(avro_idl_parse_tests).
55

66
-include("../src/idl.hrl").

test/avro_idl_tests.erl

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
%% @doc Tests for IDL converter / loader
22
%% @end
3-
%% @author Sergey Prokhhorov <me@seriyps.ru>
3+
%% @author Sergey Prokhorov <me@seriyps.ru>
44
-module(avro_idl_tests).
55

66
-include("../src/idl.hrl").
@@ -34,7 +34,7 @@ annotations_avpr_test() ->
3434
<<"namespace">> => <<"enums">>,
3535
<<"type">> => ?AVRO_ENUM,
3636
<<"name">> => <<"MyEnum">>,
37-
<<"variants">> => [<<"A">>, <<"B">>, <<"C">>]},
37+
<<"symbols">> => [<<"A">>, <<"B">>, <<"C">>]},
3838
#{<<"doc">> => <<"My Fixed">>,
3939
<<"namespace">> => <<"fixeds">>,
4040
<<"type">> => ?AVRO_FIXED,
@@ -88,7 +88,7 @@ protocol_with_typedefs_avpr_test() ->
8888
[#{<<"name">> := <<"MyEnum1">>},
8989
#{<<"name">> := <<"MyEnum2">>,
9090
<<"type">> := ?AVRO_ENUM,
91-
<<"variants">> := [<<"VAR21">>, <<"VAR22">>, <<"VAR23">>]},
91+
<<"symbols">> := [<<"VAR21">>, <<"VAR22">>, <<"VAR23">>]},
9292
#{<<"name">> := <<"MyFix">>,
9393
<<"type">> := ?AVRO_FIXED,
9494
<<"size">> := 10},
@@ -137,14 +137,14 @@ protocol_with_typedefs_avpr_test() ->
137137
Messages).
138138

139139

140-
duplicate_annotation_test() ->
140+
duplicate_annotation_avpr_test() ->
141141
?assertError(
142142
{duplicate_annotation, "my_decorator", _, _},
143143
avro_idl:str_to_avpr(
144144
"@my_decorator(\"a\") @my_decorator(\"b\") protocol MyProto{}", "")
145145
).
146146

147-
nested_complex_types_test() ->
147+
nested_complex_types_avr_test() ->
148148
?assertEqual(
149149
#{<<"protocol">> => <<"P">>,
150150
<<"messages">> => [],
@@ -165,9 +165,19 @@ nested_complex_types_test() ->
165165
"protocol P { record R { array<map<union{null, ns.T}>> f; }}", "")
166166
).
167167

168+
full_protocol_load_test() ->
169+
Schema = read_schema("full_protocol"),
170+
DecSchema = avro_idl:decode_schema(Schema, ""),
171+
_EncSchema = avro:encode_schema(DecSchema).
172+
%% ?debugFmt("~n~p~n~s", [DecSchema, EncSchema]).
173+
168174
%% Helpers
169175

170-
idl_to_avpr(Name) ->
176+
read_schema(Name) ->
171177
File = "test/data/" ++ Name ++ ".avdl",
172178
{ok, B} = file:read_file(File),
173-
avro_idl:str_to_avpr(binary_to_list(B), "").
179+
binary_to_list(B).
180+
181+
idl_to_avpr(Name) ->
182+
Schema = read_schema(Name),
183+
avro_idl:str_to_avpr(Schema, "").

0 commit comments

Comments
 (0)