-module(elvis_result). -compile({no_auto_import, [error/2]}). %% API -export([ new/3, new/4, status/1, clean/1, print_results/1 ]). -export([ get_path/1, get_rules/1, get_name/1, get_items/1, get_message/1, get_info/1, get_line_num/1 ]). %% Types -export_type([ item/0, rule/0, file/0 ]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Records %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -type item() :: #{ message => string(), info => iodata(), line_num => integer() }. -type rule() :: #{ name => atom(), items => [item()] }. -type file() :: #{ file => string(), rules => [rule()] }. -type elvis_error() :: #{ error_msg => string(), info => list() }. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Public %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% New -spec new(item, string(), [term()]) -> item() ; (rule, atom(), [item()]) -> rule() ; (file, elvis_file:file(), [elvis_error() | rule()]) -> file() ; (error, string(), string()) -> elvis_error(). new(item, Msg, Info) -> new(item, Msg, Info, 0); new(rule, Name, Results) -> #{name => Name, items => Results}; new(file, #{path := Path}, Rules) -> #{file => Path, rules => Rules}; new(error, Msg, Info) -> #{error_msg => Msg, info => Info}. -spec new(item, string(), [term()], integer()) -> item(). new(item, Msg, Info, LineNum) -> #{message => Msg, info => Info, line_num => LineNum}. %% Getters -spec get_path(file()) -> string(). get_path(#{file := File}) -> File. -spec get_rules(file()) -> [rule()]. get_rules(#{rules := Rules}) -> Rules. -spec get_name(rule()) -> atom(). get_name(#{name := Name}) -> Name. -spec get_items(rule()) -> [item()]. get_items(#{items := Items}) -> Items; get_items(_) -> []. -spec get_message(item()) -> string(). get_message(#{message := Message}) -> Message. -spec get_info(item()) -> string(). get_info(#{info := Info}) -> Info. -spec get_line_num(item()) -> integer(). get_line_num(#{line_num := LineNum}) -> LineNum. %% Print -spec print_results(file()) -> ok. print_results(Results) -> Format = application:get_env(elvis_core, output_format, colors), print(Format, Results). -spec print(plain | colors | parsable, [file()] | file()) -> ok. print(_, []) -> ok; print(Format, [Result | Results]) -> print(Format, Result), print(Format, Results); %% File print(Format, #{file := Path, rules := Rules}) -> case Format of parsable -> ok; _ -> case status(Rules) of ok -> elvis_utils:notice("# ~s [{{green-bold}}OK{{white-bold}}]", [Path]); fail -> elvis_utils:error("# ~s [{{red-bold}}FAIL{{white-bold}}]", [Path]) end end, print_rules(Format, Path, Rules); print(_, Error) -> print_error(Error). print_rules(_Format, _File, []) -> ok; print_rules(Format, File, [#{items := []} | Items]) -> print_rules(Format, File, Items); print_rules(Format, File, [#{items := Items, name := Name} | EItems]) -> case Format of parsable -> ok; _ -> elvis_utils:error(" - ~s", [atom_to_list(Name)]) end, print_item(Format, File, Name, Items), print_rules(Format, File, EItems); print_rules(Format, File, [Error | Items]) -> print_error(Error), print_rules(Format, File, Items). %% Item print_item(Format, File, Name, [#{message := Msg, line_num := Ln, info := Info} | Items]) -> case Format of parsable -> FMsg = io_lib:format(Msg, Info), io:format("~s:~p:~p:~s~n", [File, Ln, Name, FMsg]); _ -> elvis_utils:error(" - " ++ Msg, Info) end, print_item(Format, File, Name, Items); print_item(Format, File, Name, [Error|Items]) -> print_error(Error), print_item(Format, File, Name, Items); print_item(_Format, _File, _Name, []) -> ok. print_error(#{error_msg := Msg, info := Info}) -> elvis_utils:error_prn(Msg, Info). -spec status([file() | rule()]) -> ok | fail. status([]) -> ok; status([#{rules := Rules} | Files]) -> case status(Rules) of fail -> fail; ok -> status(Files) end; status([#{items := []} | Rules]) -> status(Rules); status(_Rules) -> fail. %% @doc Removes files that don't have any failures. -spec clean([file() | rule()]) -> [file() | rule()]. clean(Files)-> clean(Files, []). %% @private -spec clean([file() | rule()], [file() | rule()]) -> [file() | rule()]. clean([], Result) -> lists:reverse(Result); clean([#{rules := []} | Files], Result) -> clean(Files, Result); clean([File = #{rules := Rules} | Files], Result) -> CleanRules = clean(Rules), NewFile = File#{rules => CleanRules}, clean(Files, [NewFile | Result]); clean([#{items := []} | Rules], Result) -> clean(Rules, Result); clean([Rule | Rules], Result) -> clean(Rules, [Rule | Result]).