anchor.diff:  -export([filter_args/2, filter_user_ctx/2]).
   
   -type name() :: binary().
  --type args() :: #{binary() => binary()}.
  +-type args() :: #{field() => binary()}.
   -type params() :: #helper_params{}.
  --type user_ctx() :: #{binary() => binary() | integer()}.
  --type group_ctx() :: #{binary() => binary() | integer()}.
  --type optional_field() :: {optional, binary()}.
  +-type user_ctx() :: #{field() => binary() | integer()}.
  +-type group_ctx() :: #{field() => binary() | integer()}.
  +-type ctx() :: user_ctx() | group_ctx().
  +-type field() :: binary().
  +-type optional_field() :: {optional, field()}.
   
   -export_type([name/0, args/0, params/0, user_ctx/0, group_ctx/0]).
   
  
positive.code: %% have valid type.
  %% @end
  %%--------------------------------------------------------------------
  -spec check_user_or_group_ctx_fields([binary()], user_ctx() | group_ctx()) ->
      ok | {error, Reason :: term()}.
  check_user_or_group_ctx_fields([], Ctx) when Ctx == #{} ->
      ok;
  check_user_or_group_ctx_fields([], Ctx) ->
      {error, {invalid_additional_fields, Ctx}};
  check_user_or_group_ctx_fields([Field | Fields], Ctx) ->
      case check_ctx_field(Field, Ctx) of
          {ok, CtxRest} -> check_user_or_group_ctx_fields(Fields, CtxRest);
          Error -> Error
      end.
  
  %% @private
  -spec check_ctx_field(Key, Ctx :: #{binary() := term()}) ->
      {ok, CtxRest :: user_ctx() | group_ctx()} |
      {error, Reason :: term()} when
      Key :: binary() | optional_field().
  check_ctx_field({optional, Field}, Ctx) ->
      case check_ctx_field(Field, Ctx) of
          {error, {missing_field, _}} -> {ok, Ctx};
          Result -> Result
      end;
  
  check_ctx_field(Field, Ctx) ->
      case Ctx of
          #{Field := <<"null">>} -> {error, {invalid_field_value, Field, <<"null">>}};
          #{Field := Value} when is_binary(Value) -> {ok, maps:remove(Field, Ctx)};
          #{Field := Value} when is_integer(Value) -> {ok, maps:remove(Field, Ctx)};
          #{Field := Value} -> {error, {invalid_field_value, Field, Value}};
          #{} -> {error, {missing_field, Field}}
      end.
  
  
  %%--------------------------------------------------------------------
  %% @private
  %% @doc
  %% Validates that a storage helper arguments map contains all
  %% required fields and all its values are binaries.
  %% @end
  %%--------------------------------------------------------------------
  -spec check_args(RequiredFields :: [binary()], Args :: map()) ->
      ok | {error, {missing_field, Key :: binary()}}
      | {error, {invalid_field_value, Key :: term(), Value :: term ()}}.
  check_args(RequiredFields, Args) ->
      Result = lists:foldl(fun
          (_, {error, _} = Error) -> Error;
          (Field, ok) ->
              case maps:find(Field, Args) of
                  {ok, _Value} -> ok;
                  error -> {error, {missing_field, Field}}
              end
      end, ok, RequiredFields),
  
      case Result of
          ok ->
              % ensure all values are binaries
              maps:fold(fun
                  (Key, Value, ok) when not is_binary(Value) ->
                      {error, {invalid_field_value, Key, Value}};
                  (_, _, Result) -> Result
              end, ok, Args);
          Error -> Error
      end.
  
negative.code: %%--------------------------------------------------------------------
  -spec filter_user_ctx(name(), #{binary() => term()}) -> #{binary() => term()}.
  filter_user_ctx(HelperName, Params) ->
      AllowedFields = expected_user_ctx_params(HelperName, Params),
      filter_params(AllowedFields, Params).
  
  
  %%--------------------------------------------------------------------
  