defmodule Credo.Execution do @moduledoc """ Every run of Credo is configured via a `Execution` struct, which is created and manipulated via the `Credo.Execution` module. """ @doc """ The `Execution` struct is created and manipulated via the `Credo.Execution` module. """ defstruct argv: [], cli_options: nil, # config files: nil, color: true, debug: false, checks: nil, requires: [], strict: false, # checks if there is a new version of Credo check_for_updates: true, # options, set by the command line min_priority: 0, help: false, version: false, verbose: false, all: false, format: nil, only_checks: nil, ignore_checks: nil, crash_on_error: true, mute_exit_status: false, read_from_stdin: false, # state, which is accessed and changed over the course of Credo's execution current_task: nil, parent_task: nil, halted: false, source_files_pid: nil, issues_pid: nil, timing_pid: nil, skipped_checks: nil, assigns: %{}, results: %{}, config_comment_map: %{} @type t :: module alias Credo.Execution.ExecutionIssues alias Credo.Execution.ExecutionSourceFiles alias Credo.Execution.ExecutionTiming @doc "Builds an Execution struct for the the given `argv`." def build(argv) when is_list(argv) do %__MODULE__{argv: argv} end @doc """ Returns the checks that should be run for a given `exec` struct. Takes all checks from the `checks:` field of the exec, matches those against any patterns to include or exclude certain checks given via the command line. """ def checks(exec) def checks(%__MODULE__{ checks: checks, only_checks: only_checks, ignore_checks: ignore_checks }) do match_regexes = only_checks |> List.wrap() |> to_match_regexes ignore_regexes = ignore_checks |> List.wrap() |> to_match_regexes checks |> Enum.filter(&match_regex(&1, match_regexes, true)) |> Enum.reject(&match_regex(&1, ignore_regexes, false)) end defp match_regex(_tuple, [], default_for_empty), do: default_for_empty defp match_regex(tuple, regexes, _default_for_empty) do check_name = tuple |> Tuple.to_list() |> List.first() |> to_string Enum.any?(regexes, &Regex.run(&1, check_name)) end defp to_match_regexes(list) do Enum.map(list, fn match_check -> {:ok, match_pattern} = Regex.compile(match_check, "i") match_pattern end) end @doc """ Sets the exec values which `strict` implies (if applicable). """ def set_strict(exec) def set_strict(%__MODULE__{strict: true} = exec) do %__MODULE__{exec | all: true, min_priority: -99} end def set_strict(%__MODULE__{strict: false} = exec) do %__MODULE__{exec | min_priority: 0} end def set_strict(exec), do: exec @doc "Returns the name of the command, which should be run by the given execution." def get_command_name(exec) do exec.cli_options.command end @doc false def get_path(exec) do exec.cli_options.path end # Assigns @doc "Returns the assign with the given `name` for the given `exec` struct (or return the given `default` value)." def get_assign(exec, name, default \\ nil) do Map.get(exec.assigns, name, default) end @doc "Puts the given `value` with the given `name` as assign into the given `exec` struct." def put_assign(exec, name, value) do %__MODULE__{exec | assigns: Map.put(exec.assigns, name, value)} end # Source Files @doc "Returns all source files for the given `exec` struct." def get_source_files(exec) do Credo.Execution.ExecutionSourceFiles.get(exec) end @doc "Puts the given `source_files` into the given `exec` struct." def put_source_files(exec, source_files) do ExecutionSourceFiles.put(exec, source_files) exec end # Issues @doc "Returns all issues for the given `exec` struct." def get_issues(exec) do exec |> ExecutionIssues.to_map() |> Map.values() |> List.flatten() end @doc "Returns issues for the given `exec` struct that relate to the given `filename`." def get_issues(exec, filename) do exec |> ExecutionIssues.to_map() |> Map.get(filename, []) end @doc "Sets the issues in the given `exec` struct." def set_issues(exec, issues) do ExecutionIssues.set(exec, issues) exec end # Results @doc "Returns the result with the given `name` for the given `exec` struct (or return the given `default` value)." def get_result(exec, name, default \\ nil) do Map.get(exec.results, name, default) end @doc "Puts the given `value` with the given `name` as result into the given `exec` struct." def put_result(exec, name, value) do %__MODULE__{exec | results: Map.put(exec.results, name, value)} end # Halt @doc "Halts further execution of the process." def halt(exec) do %__MODULE__{exec | halted: true} end @doc false def start_servers(%__MODULE__{} = exec) do exec |> ExecutionSourceFiles.start_server() |> ExecutionIssues.start_server() |> ExecutionTiming.start_server() end # Task tracking @doc false def set_parent_and_current_task(exec, parent_task, current_task) do %__MODULE__{exec | parent_task: parent_task, current_task: current_task} end end