Skip to content

Commit

Permalink
Merge pull request #209 from zmstone/add-deprecated-field-attribute
Browse files Browse the repository at this point in the history
feat: add field deprecation
  • Loading branch information
zmstone authored Aug 9, 2022
2 parents 60cc625 + cb64a3b commit bf12c08
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 16 deletions.
14 changes: 13 additions & 1 deletion src/hocon_schema.erl
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
path/1,
readable_type/1,
fmt_type/2,
fmt_ref/2
fmt_ref/2,
is_deprecated/1
]).

-export([
Expand Down Expand Up @@ -119,6 +120,11 @@
desc => desc(),
%% hide it from doc generation
hidden => boolean(),
%% Set to {since, Version} to mark field as deprecated.
%% deprecated field can not be removed due to compatibility reasons.
%% The value will be dropped,
%% Deprecated fields are treated as required => {false, recursively}
deprecated => {since, binary()} | false,
%% transparent metadata
extra => map()
}.
Expand Down Expand Up @@ -506,3 +512,9 @@ fmt_ref(Ns, Name) ->
true -> Name;
false -> <<(bin(Ns))/binary, ":", (bin(Name))/binary>>
end.

%% @doc Return 'true' if the field is marked as deprecated.
-spec is_deprecated(field_schema()) -> boolean().
is_deprecated(Schema) ->
IsDeprecated = field_schema(Schema, deprecated),
IsDeprecated =/= undefined andalso IsDeprecated =/= false.
33 changes: 22 additions & 11 deletions src/hocon_schema_json.erl
Original file line number Diff line number Diff line change
Expand Up @@ -88,17 +88,28 @@ fmt_fields(Ns, [{Name, FieldSchema} | Fields], Opts) ->
end.

fmt_field(Ns, Name, FieldSchema, Opts) ->
Default = hocon_schema:field_schema(FieldSchema, default),
L = [
{name, bin(Name)},
{type, fmt_type(Ns, hocon_schema:field_schema(FieldSchema, type))},
{default, fmt_default(Default)},
{raw_default, Default},
{examples, examples(FieldSchema)},
{desc, fmt_desc(hocon_schema:field_schema(FieldSchema, desc), Opts)},
{extra, hocon_schema:field_schema(FieldSchema, extra)},
{mapping, bin(hocon_schema:field_schema(FieldSchema, mapping))}
],
L =
case hocon_schema:is_deprecated(FieldSchema) of
true ->
{since, Vsn} = hocon_schema:field_schema(FieldSchema, deprecated),
[
{name, bin(Name)},
{type, fmt_type(Ns, hocon_schema:field_schema(FieldSchema, type))},
{desc, bin(["Deprecated since ", Vsn, "."])}
];
false ->
Default = hocon_schema:field_schema(FieldSchema, default),
[
{name, bin(Name)},
{type, fmt_type(Ns, hocon_schema:field_schema(FieldSchema, type))},
{default, fmt_default(Default)},
{raw_default, Default},
{examples, examples(FieldSchema)},
{desc, fmt_desc(hocon_schema:field_schema(FieldSchema, desc), Opts)},
{extra, hocon_schema:field_schema(FieldSchema, extra)},
{mapping, bin(hocon_schema:field_schema(FieldSchema, mapping))}
]
end,
maps:from_list([{K, V} || {K, V} <- L, V =/= undefined]).

examples(FieldSchema) ->
Expand Down
24 changes: 21 additions & 3 deletions src/hocon_tconf.erl
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,15 @@ do_map2(Fields, Value0, Opts) ->
map_fields([], Conf, Mapped, _Opts) ->
{Mapped, Conf};
map_fields([{FieldName, FieldSchema} | Fields], Conf0, Acc, Opts) ->
case hocon_schema:is_deprecated(FieldSchema) of
true ->
Conf = del_value(Opts, bin(FieldName), Conf0),
map_fields(Fields, Conf, Acc, Opts);
false ->
map_fields_cont([{FieldName, FieldSchema} | Fields], Conf0, Acc, Opts)
end.

map_fields_cont([{FieldName, FieldSchema} | Fields], Conf0, Acc, Opts) ->
FieldType = field_schema(FieldSchema, type),
FieldValue = get_field(Opts, FieldName, Conf0),
NewOpts = push_stack(Opts, FieldName),
Expand Down Expand Up @@ -698,9 +707,15 @@ match_field_name(Name, ExpectedNames) ->
lists:partition(fun(N) -> bin(N) =:= bin(Name) end, ExpectedNames).

is_required(Opts, Schema) ->
case field_schema(Schema, required) of
undefined -> maps:get(required, Opts, ?DEFAULT_REQUIRED);
Maybe -> Maybe
case hocon_schema:is_deprecated(Schema) of
true ->
%% a deprecated field is never required
{false, recursively};
false ->
case field_schema(Schema, required) of
undefined -> maps:get(required, Opts, ?DEFAULT_REQUIRED);
Maybe -> Maybe
end
end.

field_schema(Sc, Key) ->
Expand Down Expand Up @@ -1005,6 +1020,9 @@ put_value(#{format := richmap} = Opts, Path, V, Conf) ->
put_value(#{format := map} = Opts, Path, V, Conf) ->
plain_put(Opts, split(Path), V, Conf).

del_value(Opts, Field, Conf) ->
boxit(Opts, maps:without([Field], unbox(Opts, Conf)), Conf).

split(Path) -> hocon_util:split_path(Path).

validators(undefined) ->
Expand Down
34 changes: 33 additions & 1 deletion test/hocon_tconf_tests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -720,9 +720,41 @@ required_test() ->
#{<<"f2">> => "string", <<"f3">> => 0},
hocon_tconf:check_plain(ScRequired, #{<<"f2">> => <<"string">>}, #{required => true})
),

ok.

deprection_test() ->
Sc = #{
roots => [
{f1, hoconsc:mk(integer())},
{f2, hoconsc:mk(string())},
{f3, hoconsc:mk(integer(), #{deprecated => {since, "0.1.1"}})}
]
},
?assertEqual(
#{<<"f2">> => "string"},
hocon_tconf:check_plain(
Sc,
#{<<"f2">> => <<"string">>},
#{required => false}
)
),
?assertEqual(
#{<<"f1">> => 1, <<"f2">> => "string"},
hocon_tconf:check_plain(
Sc,
#{<<"f1">> => 1, <<"f2">> => <<"string">>},
#{required => true}
)
),
?assertEqual(
#{<<"f2">> => "string"},
hocon_tconf:check_plain(
Sc,
#{<<"f2">> => <<"string">>, <<"f3">> => "whatever"},
#{required => false}
)
).

bad_root_test() ->
Sc = #{
roots => ["ab"],
Expand Down

0 comments on commit bf12c08

Please sign in to comment.