| %%%============================================================================ |
| %%% Licensed under the Apache License, Version 2.0 (the "License"); |
| %%% you may not use this file except in compliance with the License. |
| %%% You may obtain a copy of the License at |
| %%% |
| %%% http://www.apache.org/licenses/LICENSE-2.0 |
| %%% |
| %%% Unless required by applicable law or agreed to in writing, software |
| %%% distributed under the License is distributed on an "AS IS" BASIS, |
| %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| %%% See the License for the specific language governing permissions and |
| %%% limitations under the License. |
| %%%============================================================================ |
| |
| %%% @private |
| -module(meck_args_matcher). |
| |
| -export_type([args_spec/0]). |
| -export_type([opt_args_spec/0]). |
| -export_type([args_matcher/0]). |
| |
| %% API |
| -export([new/1]). |
| -export([arity/1]). |
| -export([match/2]). |
| |
| %%%============================================================================ |
| %%% Definitions |
| %%%============================================================================ |
| |
| -record(args_matcher, {opt_args_pattern :: opt_args_pattern(), |
| comp_match_spec :: ets:comp_match_spec(), |
| has_matchers :: boolean()}). |
| |
| %%%============================================================================ |
| %%% Types |
| %%%============================================================================ |
| |
| -type opt_args_spec() :: args_spec() | '_'. |
| -type args_spec() :: args_pattern() | non_neg_integer(). |
| -type opt_args_pattern() :: args_pattern() | '_'. |
| -type args_pattern() :: [any() | '_' | meck_matcher:matcher()]. |
| |
| -opaque args_matcher() :: #args_matcher{}. |
| |
| %%%============================================================================ |
| %%% API |
| %%%============================================================================ |
| |
| -spec new(opt_args_spec()) -> args_matcher(). |
| new('_') -> |
| MatchSpecItem = meck_util:match_spec_item({'_'}), |
| CompMatchSpec = ets:match_spec_compile([MatchSpecItem]), |
| #args_matcher{opt_args_pattern = '_', |
| comp_match_spec = CompMatchSpec, |
| has_matchers = false}; |
| new(Arity) when is_number(Arity) -> |
| ArgsPattern = lists:duplicate(Arity, '_'), |
| MatchSpecItem = meck_util:match_spec_item({ArgsPattern}), |
| CompMatchSpec = ets:match_spec_compile([MatchSpecItem]), |
| #args_matcher{opt_args_pattern = ArgsPattern, |
| comp_match_spec = CompMatchSpec, |
| has_matchers = false}; |
| new(ArgsPattern) when is_list(ArgsPattern) -> |
| {HasMatchers, Pattern} = case strip_off_matchers(ArgsPattern) of |
| unchanged -> |
| {false, ArgsPattern}; |
| StrippedArgsSpec -> |
| {true, StrippedArgsSpec} |
| end, |
| MatchSpecItem = meck_util:match_spec_item({Pattern}), |
| CompMatchSpec = ets:match_spec_compile([MatchSpecItem]), |
| #args_matcher{opt_args_pattern = ArgsPattern, |
| comp_match_spec = CompMatchSpec, |
| has_matchers = HasMatchers}. |
| |
| -spec arity(args_matcher()) -> Arity::non_neg_integer(). |
| arity(#args_matcher{opt_args_pattern = ArgsPattern}) -> |
| erlang:length(ArgsPattern). |
| |
| -spec match(Args::any(), args_matcher()) -> boolean(). |
| match(Args, #args_matcher{opt_args_pattern = OptArgsPattern, |
| comp_match_spec = CompMatchSpec, |
| has_matchers = HasMatchers}) -> |
| case ets:match_spec_run([{Args}], CompMatchSpec) of |
| [] -> |
| false; |
| _Matches when HasMatchers andalso erlang:is_list(OptArgsPattern) -> |
| check_by_matchers(Args, OptArgsPattern); |
| _Matches -> |
| true |
| end. |
| |
| %%%============================================================================ |
| %%% Internal functions |
| %%%============================================================================ |
| |
| -spec strip_off_matchers(args_pattern()) -> |
| NewArgsPattern::args_pattern() | unchanged. |
| strip_off_matchers(ArgsPattern) -> |
| strip_off_matchers(ArgsPattern, [], false). |
| |
| -spec strip_off_matchers(args_pattern(), Stripped::[any() | '_'], boolean()) -> |
| NewArgsPattern::args_pattern() | unchanged. |
| strip_off_matchers([ArgPattern | Rest], Stripped, HasMatchers) -> |
| case meck_matcher:is_matcher(ArgPattern) of |
| true -> |
| strip_off_matchers(Rest, ['_' | Stripped], true); |
| _ -> |
| strip_off_matchers(Rest, [ArgPattern | Stripped], HasMatchers) |
| end; |
| strip_off_matchers([], Stripped, true) -> |
| lists:reverse(Stripped); |
| strip_off_matchers([], _Stripped, false) -> |
| unchanged. |
| |
| -spec check_by_matchers(Args ::[any()], MaybeMatchers::[any()]) -> boolean(). |
| check_by_matchers([Arg | RestArgs], [MaybeMatcher | RestMaybeMatchers]) -> |
| case meck_matcher:match_ignore(Arg, MaybeMatcher) of |
| true -> |
| check_by_matchers(RestArgs, RestMaybeMatchers); |
| _Else -> |
| false |
| end; |
| check_by_matchers([], []) -> |
| true. |