diff --git a/include/hocon_types.hrl b/include/hocon_types.hrl index 21505bf..b258eb1 100644 --- a/include/hocon_types.hrl +++ b/include/hocon_types.hrl @@ -18,7 +18,8 @@ -define(HOCONSC_TYPES_HRL, true). -define(ARRAY(OfTYpe), {array, OfTYpe}). --define(UNION(OfTypes), {union, OfTypes}). +-define(UNION(OfTypes), {union, OfTypes, undefined}). +-define(UNION(OfTypes, DisplayName), {union, OfTypes, DisplayName}). -define(ENUM(OfSymbols), {enum, OfSymbols}). -define(REF(Name), {ref, Name}). % remote ref diff --git a/sample-schemas/demo_schema.erl b/sample-schemas/demo_schema.erl index bacc902..7885563 100644 --- a/sample-schemas/demo_schema.erl +++ b/sample-schemas/demo_schema.erl @@ -38,7 +38,7 @@ fields("a_b") -> ]; fields("b") -> - [ {"u", fun (type) -> {union, ["priv.bool", "priv.int"]}; + [ {"u", fun (type) -> hoconsc:union(["priv.bool", "priv.int"]); (mapping) -> "app_foo.u"; (_) -> undefined end} , {"arr", fun (type) -> hoconsc:array("priv.int"); diff --git a/src/hocon_schema.erl b/src/hocon_schema.erl index 560fbb9..c2add48 100644 --- a/src/hocon_schema.erl +++ b/src/hocon_schema.erl @@ -95,13 +95,13 @@ %% array of | ?ARRAY(type()) %% one-of - | ?UNION(union_members()) + | ?UNION(union_members(), _) %% one-of atoms, data is allowed to be binary() | ?ENUM([atom()]). -type field_schema() :: typerefl:type() - | ?UNION(union_members()) + | ?UNION(union_members(), _) | ?ARRAY(type()) | ?ENUM(type()) | field_schema_map() @@ -340,7 +340,7 @@ find_structs_per_type(Schema, ?LAZY(Type), Acc, Stack, TStack, Opts) -> find_structs_per_type(Schema, Type, Acc, Stack, TStack, Opts); find_structs_per_type(Schema, ?ARRAY(Type), Acc, Stack, TStack, Opts) -> find_structs_per_type(Schema, Type, Acc, ["$INDEX" | Stack], TStack, Opts); -find_structs_per_type(Schema, ?UNION(Types0), Acc, Stack, TStack, Opts) -> +find_structs_per_type(Schema, ?UNION(Types0, _), Acc, Stack, TStack, Opts) -> Types = hoconsc:union_members(Types0), lists:foldl( fun(T, AccIn) -> @@ -442,7 +442,7 @@ field_schema(?R_REF(_, _) = Ref, SchemaKey) -> field_schema(hoconsc:mk(Ref), SchemaKey); field_schema(?ARRAY(_) = Array, SchemaKey) -> field_schema(hoconsc:mk(Array), SchemaKey); -field_schema(?UNION(_) = Union, SchemaKey) -> +field_schema(?UNION(_, _) = Union, SchemaKey) -> field_schema(hoconsc:mk(Union), SchemaKey); field_schema(?ENUM(_) = Enum, SchemaKey) -> field_schema(hoconsc:mk(Enum), SchemaKey); @@ -476,10 +476,11 @@ fmt_type(Ns, ?ARRAY(T)) -> kind => array, elements => fmt_type(Ns, T) }; -fmt_type(Ns, ?UNION(Ts)) -> +fmt_type(Ns, ?UNION(Ts, DisplayName)) -> #{ kind => union, - members => [fmt_type(Ns, T) || T <- hoconsc:union_members(Ts)] + members => [fmt_type(Ns, T) || T <- hoconsc:union_members(Ts)], + dispay_name => DisplayName }; fmt_type(_Ns, ?ENUM(Symbols)) -> #{ diff --git a/src/hocon_schema_example.erl b/src/hocon_schema_example.erl index 47b8a67..73e943e 100644 --- a/src/hocon_schema_example.erl +++ b/src/hocon_schema_example.erl @@ -90,7 +90,7 @@ fmt_field( case maps:get(examples, Field, #{}) of #{} = Example -> fmt_field_with_example(Path, SubFields, Example, Opts2); - {union, UnionExamples} -> + ?UNION(UnionExamples, _) -> Examples1 = filter_union_example(UnionExamples, SubFields), fmt_field_with_example(Path, SubFields, Examples1, Opts2); {array, ArrayExamples} -> diff --git a/src/hocon_tconf.erl b/src/hocon_tconf.erl index 4b07c6b..5beb7aa 100644 --- a/src/hocon_tconf.erl +++ b/src/hocon_tconf.erl @@ -587,7 +587,7 @@ map_field(?REF(Ref), FieldSchema, Value, #{schema := Schema} = Opts) -> map_field(Ref, FieldSchema, Value, #{schema := Schema} = Opts) when is_list(Ref) -> Fields = hocon_schema:fields(Schema, Ref), do_map(Fields, Value, Opts, FieldSchema); -map_field(?UNION(Types0), Schema0, Value, Opts) -> +map_field(?UNION(Types0, _), Schema0, Value, Opts) -> try select_union_members(Types0, Value, Opts) of Types -> F = fun(Type) -> @@ -898,7 +898,7 @@ is_path(Schema, ?ARRAY(Type), [Name | Path]) -> {true, _} -> is_path(Schema, Type, Path); false -> false end; -is_path(Schema, ?UNION(Types), Path) -> +is_path(Schema, ?UNION(Types, _), Path) -> lists:any(fun(T) -> is_path(Schema, T, Path) end, hoconsc:union_members(Types)); is_path(Schema, ?MAP(_, Type), [_ | Path]) -> is_path(Schema, Type, Path); diff --git a/src/hoconsc.erl b/src/hoconsc.erl index a3d8539..540a36e 100644 --- a/src/hoconsc.erl +++ b/src/hoconsc.erl @@ -19,7 +19,7 @@ -export([mk/1, mk/2]). -export([ref/1, ref/2]). --export([array/1, union/1, enum/1]). +-export([array/1, union/1, union/2, enum/1]). -export([lazy/1, map/2]). -export([is_schema/1]). -export([union_members/1]). @@ -59,7 +59,11 @@ array(OfType) -> ?ARRAY(OfType). %% `({value, #{<<"kind">> := <<"foo">>}) -> [ref(foo)];' %% `({value, #{<<"kind">> := <<"bar">>}} -> [ref(bar)].' -spec union(hocon_schema:union_members()) -> ?UNION(hocon_schema:union_members()). -union(OfTypes) when is_list(OfTypes) orelse is_function(OfTypes, 1) -> ?UNION(OfTypes). +union(OfTypes) when is_list(OfTypes) orelse is_function(OfTypes, 1) -> + ?UNION(OfTypes, undefined). + +union(OfTypes, DisplayName) when is_list(OfTypes) orelse is_function(OfTypes, 1) -> + ?UNION(OfTypes, DisplayName). %% @doc make a enum type. enum(OfSymbols) when is_list(OfSymbols) -> ?ENUM(OfSymbols). @@ -71,7 +75,7 @@ lazy(HintType) -> ?LAZY(HintType). map(Name, Type) -> ?MAP(Name, Type). %% @doc Check Type is a hocon type. -is_schema(?UNION(Members)) -> lists:all(fun is_schema/1, union_members(Members)); +is_schema(?UNION(Members, _)) -> lists:all(fun is_schema/1, union_members(Members)); is_schema(?ARRAY(ElemT)) -> is_schema(ElemT); is_schema(?LAZY(HintT)) -> is_schema(HintT); is_schema(?REF(_)) -> true; @@ -86,7 +90,7 @@ is_schema(_) -> false. assert_type(S) when is_function(S) -> error({expecting_type_but_got_schema, S}); assert_type(#{type := _} = S) -> error({expecting_type_but_got_schema, S}); -assert_type(?UNION(Members)) -> +assert_type(?UNION(Members, _)) -> lists:foreach(fun assert_type/1, union_members(Members)); assert_type(?ENUM(Symbols)) -> lists:foreach( diff --git a/test/hocon_tconf_tests.erl b/test/hocon_tconf_tests.erl index 2fd1d91..d628abe 100644 --- a/test/hocon_tconf_tests.erl +++ b/test/hocon_tconf_tests.erl @@ -590,7 +590,7 @@ with_envs(Fun, Envs) -> hocon_test_lib:with_envs(Fun, Envs). with_envs(Fun, Args, Envs) -> hocon_test_lib:with_envs(Fun, Args, Envs). union_as_enum_test() -> - Sc = #{roots => [{enum, hoconsc:union([a, b, c])}]}, + Sc = #{roots => [{enum, hoconsc:union([a, b, c], <<"string()">>)}]}, ?assertEqual( #{<<"enum">> => a}, hocon_tconf:check_plain(Sc, #{<<"enum">> => a})