Packages
elvis_core
0.3.2
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_core.erl
-module(elvis_core).
%% Public API
-export([
rock/0,
rock/1,
rock_this/1,
rock_this/2
]).
-export([start/0]).
-define(APP_NAME, "elvis").
-type source_filename() :: nonempty_string().
-type target() :: source_filename() | module().
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Public API
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% @doc Used when starting the application on the shell.
-spec start() -> ok.
start() ->
{ok, _} = application:ensure_all_started(elvis),
ok.
%%% Rock Command
-spec rock() -> ok | {fail, [elvis_result:file()]}.
rock() ->
Config = elvis_config:default(),
rock(Config).
-spec rock(elvis_config:config()) -> ok | {fail, [elvis_result:file()]}.
rock(Config) ->
ok = elvis_config:validate(Config),
NewConfig = elvis_config:normalize(Config),
Results = lists:map(fun do_rock/1, NewConfig),
lists:foldl(fun combine_results/2, ok, Results).
-spec rock_this(target()) ->
ok | {fail, elvis_result:file()}.
rock_this(Target) ->
Config = elvis_config:default(),
rock_this(Target, Config).
-spec rock_this(target(), elvis_config:config()) ->
ok | {fail, elvis_result:file()}.
rock_this(Module, Config) when is_atom(Module) ->
ModuleInfo = Module:module_info(compile),
Path = proplists:get_value(source, ModuleInfo),
rock_this(Path, Config);
rock_this(Path, Config) ->
elvis_config:validate(Config),
NewConfig = elvis_config:normalize(Config),
Dirname = filename:dirname(Path),
Filename = filename:basename(Path),
File = case elvis_file:find_files([Dirname], Filename) of
[] -> throw({enoent, Path});
[File0] -> File0
end,
FilterFun = fun(Cfg) ->
Filter = elvis_config:filter(Cfg),
Dirs = elvis_config:dirs(Cfg),
IgnoreList = elvis_config:ignore(Cfg),
[] =/= elvis_file:filter_files([File], Dirs, Filter,
IgnoreList)
end,
FilteredConfig = lists:filter(FilterFun, NewConfig),
LoadedFile = load_file_data(FilteredConfig, File),
ApplyRulesFun = fun(Cfg) -> apply_rules(Cfg, LoadedFile) end,
Results = lists:map(ApplyRulesFun, FilteredConfig),
elvis_result_status(Results).
%% @private
-spec do_rock(map()) -> ok | {fail, [elvis_result:file()]}.
do_rock(Config0) ->
elvis_utils:info("Loading files..."),
Config = elvis_config:resolve_files(Config0),
Files = elvis_config:files(Config),
Fun = fun (File) -> load_file_data(Config, File) end,
LoadedFiles = lists:map(Fun, Files),
elvis_utils:info("Applying rules..."),
Results = [apply_rules(Config, File) || File <- LoadedFiles],
elvis_result_status(Results).
%% @private
-spec load_file_data(map() | [map()], elvis_file:file()) -> elvis_file:file().
load_file_data(Config, File) ->
Path = elvis_file:path(File),
elvis_utils:info("Loading ~s", [Path]),
try
elvis_file:load_file_data(Config, File)
catch
_:Reason ->
Msg = "~p when loading file ~p.",
elvis_utils:error_prn(Msg, [Reason, Path]),
File
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Private
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec combine_results(ok | {fail, [elvis_result:file()]},
ok | {fail, [elvis_result:file()]}) ->
ok | {fail, [elvis_result:file()]}.
combine_results(ok, Acc) ->
Acc;
combine_results(Item, ok) ->
Item;
combine_results({fail, ItemResults}, {fail, AccResults}) ->
{fail, ItemResults ++ AccResults}.
-spec apply_rules(map(), File::elvis_file:file()) ->
elvis_result:file().
apply_rules(Config, File) ->
Rules = elvis_config:rules(Config),
Acc = {[], Config, File},
{RulesResults, _, _} = lists:foldl(fun apply_rule/2, Acc, Rules),
Results = elvis_result:new(file, File, RulesResults),
elvis_result:print(Results),
Results.
apply_rule({Module, Function}, {Result, Config, File}) ->
apply_rule({Module, Function, #{}}, {Result, Config, File});
apply_rule({Module, Function, ConfigArgs}, {Result, Config, File}) ->
ConfigMap =
try
ensure_config_map(Module, Function, ConfigArgs)
catch
_:function_clause ->
throw({invalid_config, disable_without_ruleset})
end,
RuleResult = try
Results = Module:Function(Config, File, ConfigMap),
SortFun = fun(#{line_num := L1}, #{line_num := L2}) ->
L1 =< L2
end,
SortResults = lists:sort(SortFun, Results),
elvis_result:new(rule, Function, SortResults)
catch
_:Reason ->
Msg = "'~p' while applying rule '~p'.",
elvis_result:new(error, Msg, [Reason, Function])
end,
{[RuleResult | Result], Config, File}.
%% @doc Process a tules configuration argument and converts it to a map.
ensure_config_map(_, _, Map) when is_map(Map) ->
Map;
ensure_config_map(elvis_style, line_length, [Limit]) ->
#{limit => Limit};
ensure_config_map(elvis_style, operator_spaces, Rules) ->
#{rules => Rules};
ensure_config_map(elvis_style, nesting_level, [Level]) ->
#{level => Level};
ensure_config_map(elvis_style, god_modules, [Limit]) ->
#{limit => Limit};
ensure_config_map(elvis_style, god_modules, [Limit, IgnoreModules]) ->
#{limit => Limit, ignore => IgnoreModules};
ensure_config_map(elvis_style, invalid_dynamic_call, IgnoreModules) ->
#{ignore => IgnoreModules};
ensure_config_map(elvis_style,
module_naming_convention,
[Regex, IgnoreModules]) ->
#{regex => Regex, ignore => IgnoreModules};
ensure_config_map(_, _, []) ->
#{}.
%% @private
elvis_result_status(Results) ->
case elvis_result:status(Results) of
fail -> {fail, elvis_result:clean(Results)};
ok -> ok
end.