Skip to content

Commit 91c6176

Browse files
authored
Merge pull request #130 from zmstone/allow-empty-union
Allow empty union
2 parents d6bd9d4 + 7f17926 commit 91c6176

File tree

6 files changed

+23
-24
lines changed

6 files changed

+23
-24
lines changed

changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
* 2.10.3
2+
- Allow union type to have zero member types.
13
* 2.10.2
24
- Fix bytes and fixed JSON value decode
35
* 2.10.1

src/avro_union.erl

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@
5454

5555
-include("avro_internal.hrl").
5656

57-
-type id2type() :: gb_trees:tree(union_index(), type_or_name()).
58-
-type name2id() :: gb_trees:tree(name(), {union_index(), boolean()}).
57+
-type id2type() :: #{union_index() => type_or_name()}.
58+
-type name2id() :: #{name() => {union_index(), boolean()}}.
5959
-type encode_result() :: avro_binary() | avro_json().
6060
-type encode_fun() :: fun((avro_type(), avro:in(),
6161
union_index()) -> encode_result()).
@@ -64,13 +64,15 @@
6464

6565
%% @doc Define a union type.
6666
%% Exception when any of the below constraints is violated:
67-
%% 1. A union should have at least one member and no duplication
68-
%% 2. Union should no have union as direct member
69-
%% 3. No duplicated types are allowed in members
67+
%% 1. Union should no have union as direct member
68+
%% 2. No duplicated types are allowed in members
7069
%% @end
7170
-spec type([type_or_name()]) -> union_type() | no_return().
7271
type([]) ->
73-
erlang:error(<<"union should have at least one member type">>);
72+
#avro_union_type
73+
{ id2type = #{}
74+
, name2id = #{}
75+
};
7476
type([_ | _ ] = Types0) ->
7577
IsUnion = fun(T) -> ?IS_UNION_TYPE(T) end,
7678
lists:any(IsUnion, Types0) andalso
@@ -81,8 +83,8 @@ type([_ | _ ] = Types0) ->
8183
Name2Id = build_name_to_id(IndexedTypes),
8284
ok = assert_no_duplicated_names(Name2Id, []),
8385
#avro_union_type
84-
{ id2type = gb_trees:from_orddict(IndexedTypes)
85-
, name2id = gb_trees:from_orddict(orddict:from_list(Name2Id))
86+
{ id2type = maps:from_list(IndexedTypes)
87+
, name2id = maps:from_list(Name2Id)
8688
}.
8789

8890
%% @doc Resolve fullname by newly discovered enclosing namespace.
@@ -103,17 +105,14 @@ update_member_types(T0, F) ->
103105
%% @doc Get the union member types in a list.
104106
-spec get_types(union_type()) -> [avro_type()].
105107
get_types(#avro_union_type{id2type = IndexedTypes}) ->
106-
{_Ids, Types} = lists:unzip(gb_trees:to_list(IndexedTypes)),
108+
{_Ids, Types} = lists:unzip(lists:keysort(1, maps:to_list(IndexedTypes))),
107109
Types.
108110

109111
%% @doc Search for a union member by its index or full name.
110112
-spec lookup_type(name_raw() | union_index(), union_type()) ->
111113
{ok, avro_type() | name()} | false.
112114
lookup_type(Id, #avro_union_type{id2type = Types}) when is_integer(Id) ->
113-
case gb_trees:lookup(Id, Types) of
114-
{value, Type} -> {ok, Type};
115-
none -> false
116-
end;
115+
maps:is_key(Id, Types) andalso {ok, maps:get(Id, Types)};
117116
lookup_type(Name, Union) when ?IS_NAME(Name) ->
118117
case lookup_index(Name, Union) of
119118
{ok, {_Id, true}} -> {ok, avro:name2type(Name)};
@@ -241,10 +240,7 @@ try_encode_union_loop(UnionType, [MemberT | Rest], Value, Index, EncodeFun) ->
241240
-spec lookup_index(name(), union_type()) ->
242241
{ok, {union_index(), boolean()}} | false.
243242
lookup_index(Name, #avro_union_type{name2id = Ids}) when ?IS_NAME_RAW(Name) ->
244-
case gb_trees:lookup(?NAME(Name), Ids) of
245-
{value, Id} -> {ok, Id};
246-
none -> false
247-
end.
243+
maps:is_key(?NAME(Name), Ids) andalso {ok, maps:get(?NAME(Name), Ids)}.
248244

249245
%% @private
250246
-spec do_cast(union_type(), {name(), avro_value()} | avro:in()) ->

test/avro_ocf_tests.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ decoder_hook_test() ->
172172
?assertEqual([[{<<"f1">>, 1}, {<<"f2">>, <<"modifiedNull">>}]], Objs).
173173

174174
test_data(FileName) ->
175-
filename:join([code:lib_dir(erlavro, test), "data", FileName]).
175+
filename:join([code:lib_dir(erlavro), "test", "data", FileName]).
176176

177177
%%%_* Emacs ====================================================================
178178
%%% Local Variables:

test/avro_schema_store_tests.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ flat_test_record() ->
216216
]).
217217

218218
test_data(FileName) ->
219-
filename:join([code:lib_dir(erlavro, test), "data", FileName]).
219+
filename:join([code:lib_dir(erlavro), "test", "data", FileName]).
220220

221221
define_field(Name, Type) -> avro_record:define_field(Name, Type).
222222

test/avro_tests.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ default_values_with_map_type_test() ->
531531
ok.
532532

533533
test_data(FileName) ->
534-
filename:join([code:lib_dir(erlavro, test), "data", FileName]).
534+
filename:join([code:lib_dir(erlavro), "test", "data", FileName]).
535535

536536
%%%_* Emacs ====================================================================
537537
%%% Local Variables:

test/avro_union_tests.erl

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,6 @@ to_term_test() ->
7979
?assertEqual(null, avro:to_term(Value1)),
8080
?assertEqual(1, avro:to_term(Value2)).
8181

82-
empty_unon_not_allowed_test() ->
83-
?assertException(error, <<"union should have at least one member type">>,
84-
avro_union:type([])).
85-
8682
cast_test() ->
8783
Type = avro_union:type([null, long]),
8884
?assertMatch({ok, #avro_value{}}, avro_union:cast(Type, {long, 1})),
@@ -158,6 +154,11 @@ union_of_string_and_int_array_test() ->
158154
avro_union:encode(Union, <<"abc">>, StringEncodeFun)),
159155
ok.
160156

157+
no_member_test() ->
158+
Union = avro_union:type([]),
159+
F = fun(_StringType, _StringValue, 0) -> error(unexpected) end,
160+
?assertError({unknown_member, _, string}, avro_union:encode(Union, {string, "abc"}, F)).
161+
161162
%%%_* Emacs ====================================================================
162163
%%% Local Variables:
163164
%%% allout-layout: t

0 commit comments

Comments
 (0)