diff --git a/SCHEMA.md b/SCHEMA.md index 04171f2..29042f9 100644 --- a/SCHEMA.md +++ b/SCHEMA.md @@ -179,7 +179,7 @@ min_max(Conf) -> Max = hocon_maps:get("foo.max", Conf), case Min =< Max of true -> ok %% return true | ok to pass this validation - false -> "min > max is not allowed" + false -> "min > max is not allowed" %% or If you need to return early, use throw(Reason) end. ``` diff --git a/src/hocon_tconf.erl b/src/hocon_tconf.erl index 6772e45..3a3bf4a 100644 --- a/src/hocon_tconf.erl +++ b/src/hocon_tconf.erl @@ -144,6 +144,14 @@ do_translate([{MappedField, Translator} | More], TrNamespace, Conf, Acc) -> Value -> do_translate(More, TrNamespace, Conf, [{string:tokens(MappedField0, "."), Value} | Acc]) catch + throw:Reason -> + Error = + {error, + ?TRANSLATION_ERRS(#{ + reason => Reason, + path => MappedField0 + })}, + do_translate(More, TrNamespace, Conf, [Error | Acc]); Exception:Reason:St -> Error = {error, @@ -173,20 +181,10 @@ assert_integrity(Schema, [{Name, Validator} | Rest], Conf, Acc) -> OK when OK =:= true orelse OK =:= ok -> assert_integrity(Schema, Rest, Conf, Acc); Other -> - assert_integrity( - Schema, - Rest, - Conf, - [ - {error, - ?VALIDATION_ERRS(#{ - reason => integrity_validation_failure, - validation_name => Name, - result => Other - })} - ] - ) + assert_integrity_failure(Schema, Rest, Conf, Name, Other) catch + throw:Reason -> + assert_integrity_failure(Schema, Rest, Conf, Name, Reason); Exception:Reason:St -> Error = {error, @@ -199,6 +197,21 @@ assert_integrity(Schema, [{Name, Validator} | Rest], Conf, Acc) -> assert_integrity(Schema, Rest, Conf, [Error | Acc]) end. +assert_integrity_failure(Schema, Rest, Conf, Name, Reason) -> + assert_integrity( + Schema, + Rest, + Conf, + [ + {error, + ?VALIDATION_ERRS(#{ + reason => integrity_validation_failure, + validation_name => Name, + result => Reason + })} + ] + ). + merge_opts(Default, Opts) -> maps:merge( Default#{ @@ -1039,6 +1052,8 @@ do_validate(Opts, Schema, Value, [H | T]) -> {error, Reason} -> validation_errs(Opts, Reason, obfuscate(Schema, Value)) catch + throw:Reason -> + validation_errs(Opts, Reason, obfuscate(Schema, Value)); C:E:St -> validation_errs( Opts, diff --git a/test/hocon_tconf_tests.erl b/test/hocon_tconf_tests.erl index 03c8d8b..c4908c2 100644 --- a/test/hocon_tconf_tests.erl +++ b/test/hocon_tconf_tests.erl @@ -767,7 +767,22 @@ translation_crash_test() -> {ok, Data} = hocon:binary("f1=12,f2=foo", #{format => richmap}), {Mapped, Conf} = hocon_tconf:map(Sc, Data), ?assertThrow( - {_, [{translation_error, #{reason := always, exception := error}}]}, + {_, [{translation_error, #{reason := always, exception := error, stacktrace := _}}]}, + hocon_tconf:translate(Sc, Conf, Mapped) + ). + +translation_throw_test() -> + Sc = #{ + roots => [ + {f1, hoconsc:mk(integer())}, + {f2, hoconsc:mk(string())} + ], + translations => #{"tr1" => [{"f3", fun(_Conf) -> throw(expect) end}]} + }, + {ok, Data} = hocon:binary("f1=12,f2=foo", #{format => richmap}), + {Mapped, Conf} = hocon_tconf:map(Sc, Data), + ?assertThrow( + {_, [{translation_error, #{reason := expect}}]}, hocon_tconf:translate(Sc, Conf, Mapped) ). @@ -1022,6 +1037,23 @@ integrity_crash_test() -> ), ok. +integrity_throw_test() -> + Sc = #{ + roots => [root], + fields => #{root => [{f1, integer()}]}, + validations => [{"always-throw", fun(_) -> throw(expect) end}] + }, + Data1 = "root={f1=1}", + ?VALIDATION_ERR( + #{ + reason := integrity_validation_failure, + validation_name := "always-throw", + result := expect + }, + check_plain_bin(Sc, Data1, #{atom_key => true}) + ), + ok. + check_plain_bin(Sc, Data, Opts) -> {ok, Conf} = hocon:binary(Data, #{}), hocon_tconf:check_plain(Sc, Conf, Opts).