Packages
elvis_core
3.2.0
5.0.4
5.0.3
5.0.2
5.0.1
5.0.0
retired
4.2.3
4.2.2
4.2.1
4.2.0
4.1.1
4.1.0
4.0.0
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.1.0
3.0.1
3.0.0
2.0.1
2.0.0
1.4.0
1.3.2
1.3.1
1.3.0
1.2.0
1.1.2
1.1.1
1.1.0
1.0.0
0.7.0
0.6.1
0.6.0
0.5.0
0.4.3
0.4.2
0.4.1
0.4.0
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
Core library for the Erlang style reviewer
Current section
Files
Jump to
Current section
Files
src/elvis_project.erl
-module(elvis_project).
-export([default/1, no_branch_deps/3, protocol_for_deps/3, old_configuration_format/3]).
-export_type([protocol_for_deps_config/0]).
-define(DEP_BRANCH,
"Dependency '~s' uses a branch. "
"Please change this to a tag or specific commit.").
-define(DEP_NO_GIT,
"Dependency '~s' is not using appropriate protocol, "
"please change this to something like '~s'").
-define(OLD_CONFIG_FORMAT,
"The current Elvis configuration file has an outdated format. "
"Please check Elvis's GitHub repository to find out what the "
"new format is.").
% These are part of a non-declared "behaviour"
% The reason why we don't try to handle them with different arity is
% that arguments are ignored in different positions (1 and 3) so that'd
% probably be messier than to ignore the warning
-hank([{unnecessary_function_arguments,
[{old_configuration_format, 3}, {no_branch_deps, 3}, {protocol_for_deps, 3}]}]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Default values
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec default(Rule :: atom()) -> DefaultRuleConfig :: term().
default(no_branch_deps) ->
#{ignore => []};
default(protocol_for_deps) ->
#{ignore => [], regex => "(https://.*|[0-9]+([.][0-9]+)*)"};
default(old_configuration_format) ->
#{}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Rules
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-type protocol_for_deps_config() :: #{ignore => [module()], regex => string()}.
-spec protocol_for_deps(elvis_config:config(),
elvis_file:file(),
protocol_for_deps_config()) ->
[elvis_result:item()].
protocol_for_deps(_Config, Target, RuleConfig) ->
IgnoreDeps = option(ignore, RuleConfig, protocol_for_deps),
Regex = option(regex, RuleConfig, protocol_for_deps),
Deps = get_deps(Target),
NoHexDeps = lists:filter(fun(Dep) -> not is_hex_dep(Dep) end, Deps),
BadDeps = lists:filter(fun(Dep) -> is_not_git_dep(Dep, Regex) end, NoHexDeps),
lists:flatmap(fun(Line) -> dep_to_result(Line, ?DEP_NO_GIT, {IgnoreDeps, Regex}) end,
BadDeps).
-spec no_branch_deps(elvis_config:config(),
elvis_file:file(),
elvis_style:empty_rule_config()) ->
[elvis_result:item()].
no_branch_deps(_Config, Target, RuleConfig) ->
IgnoreDeps = option(ignore, RuleConfig, no_branch_deps),
Deps = get_deps(Target),
BadDeps = lists:filter(fun is_branch_dep/1, Deps),
lists:flatmap(fun(Line) -> dep_to_result(Line, ?DEP_BRANCH, IgnoreDeps) end, BadDeps).
-spec old_configuration_format(elvis_config:config(),
elvis_file:file(),
elvis_style:empty_rule_config()) ->
[elvis_result:item()].
old_configuration_format(_Config, Target, _RuleConfig) ->
{Content, _} = elvis_file:src(Target),
[AllConfig] = ktn_code:consult(Content),
case proplists:get_value(elvis, AllConfig) of
undefined ->
[];
ElvisConfig ->
case is_old_config(ElvisConfig) of
false ->
[];
true ->
[elvis_result:new(item, ?OLD_CONFIG_FORMAT, [])]
end
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Private
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Rebar
%% @private
get_deps(File) ->
{Src, _} = elvis_file:src(File),
Terms = ktn_code:consult(Src),
IsDepsTerm =
fun ({deps, _}) ->
true;
(_) ->
false
end,
case lists:filter(IsDepsTerm, Terms) of
[] ->
[];
[{deps, Deps}] ->
Deps
end.
%% Rebar3
%% @private
is_branch_dep({_AppName, {_SCM, _Location, {branch, _}}}) ->
true;
is_branch_dep({AppName, {raw, DepResourceSpecification}}) ->
is_branch_dep({AppName, DepResourceSpecification});
%% Rebar2
%% @private
is_branch_dep({_AppName, _Vsn, {_SCM, _Location, {branch, _}}}) ->
true;
is_branch_dep(_) ->
false.
%% @private
is_hex_dep(_AppName) when is_atom(_AppName) ->
true;
is_hex_dep({_AppName, _Vsn, {pkg, _PackageName}})
when is_atom(_AppName), is_list(_Vsn), is_atom(_PackageName) ->
true;
is_hex_dep({_AppName, _Vsn}) when is_atom(_AppName), is_list(_Vsn) ->
true;
is_hex_dep(_) ->
false.
%% @private
is_not_git_dep({_AppName, {pkg, _OtherName}}, _Regex) ->
false;
is_not_git_dep({_AppName, {_SCM, Url, _Branch}}, Regex) ->
nomatch == re:run(Url, Regex, []);
is_not_git_dep({AppName, {raw, DepResourceSpecification}}, Regex) ->
is_not_git_dep({AppName, DepResourceSpecification}, Regex);
is_not_git_dep({_AppName, {_SCM, Url}}, Regex) ->
nomatch == re:run(Url, Regex, []);
is_not_git_dep({_AppName, _Vsn, {_SCM, Url}}, Regex) ->
nomatch == re:run(Url, Regex, []);
is_not_git_dep({_AppName, _Vsn, {_SCM, Url, _Branch}}, Regex) ->
nomatch == re:run(Url, Regex, []).
%% @private
dep_to_result({AppName, _}, Message, {IgnoreDeps, Regex}) ->
case lists:member(AppName, IgnoreDeps) of
true ->
[];
false ->
[elvis_result:new(item, Message, [AppName, Regex])]
end;
dep_to_result({AppName, _}, Message, IgnoreDeps) ->
case lists:member(AppName, IgnoreDeps) of
true ->
[];
false ->
[elvis_result:new(item, Message, [AppName])]
end;
dep_to_result({AppName, _, GitInfo}, Message, {IgnoreDeps, Regex}) ->
dep_to_result({AppName, GitInfo}, Message, {IgnoreDeps, Regex});
dep_to_result({AppName, _, GitInfo}, Message, IgnoreDeps) ->
dep_to_result({AppName, GitInfo}, Message, IgnoreDeps).
%% Old config
%% @private
is_old_config(ElvisConfig) ->
case proplists:get_value(config, ElvisConfig) of
undefined ->
false;
Config when is_map(Config) ->
true;
Config when is_list(Config) ->
SrcDirsIsKey =
fun(RuleGroup) -> maps:is_key(src_dirs, RuleGroup) orelse exists_old_rule(RuleGroup)
end,
lists:filter(SrcDirsIsKey, Config) /= []
end.
%% @private
exists_old_rule(#{rules := Rules}) ->
Filter =
fun ({_, _, Args}) when is_list(Args) ->
true;
(_) ->
false
end,
lists:filter(Filter, Rules) /= [];
exists_old_rule(_) ->
false.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Internal Function Definitions
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec option(OptionName, RuleConfig, Rule) -> OptionValue
when OptionName :: atom(),
RuleConfig :: elvis_config:config(),
Rule :: atom(),
OptionValue :: term().
option(OptionName, RuleConfig, Rule) ->
maybe_default_option(maps:get(OptionName, RuleConfig, undefined), OptionName, Rule).
-spec maybe_default_option(UserDefinedOptionValue, OptionName, Rule) -> OptionValue
when UserDefinedOptionValue :: undefined | term(),
OptionName :: atom(),
Rule :: atom(),
OptionValue :: term().
maybe_default_option(undefined = _UserDefinedOptionValue, OptionName, Rule) ->
maps:get(OptionName, default(Rule));
maybe_default_option(UserDefinedOptionValue, _OptionName, _Rule) ->
UserDefinedOptionValue.