lib/mix/tasks/archeometer.collect.credo_issues.ex

defmodule Mix.Tasks.Arch.Collect.CredoIssues do
  @moduledoc """
  Mix Task to run Credo and save the issues it found.

  Usage:

      mix arch.collect.credo_issues [options]

  The following options are accepted:

    * `--include-deps` - Wildcard glob style to filter dependencies
      This option is accepted but currently ignored.

  For more information see:
  [Basic usage guide](guides/introduction/basic_usage.md#include-dependencies-in-the-analysis)
  """

  use Mix.Task
  alias Archeometer.Util.DumpStats
  alias Archeometer.Collect.{CredoIssues, Project}

  @impl Mix.Task
  def run(argv) do
    case get_args(argv) do
      {:error, error} ->
        Mix.shell().error("Error: #{error}")
        print_help()
        {:error, error}

      # XXX: properly implement support for external apps
      [include_deps: _deps_filter] ->
        if Archeometer.Repo.db_ready?(:basic) do
          CredoIssues.collect()
          |> Map.get(:credo_issues)
          |> DumpStats.save_credo_issues()

          :ok
        else
          Mix.shell().error("Please run static analysis first")
          print_help()
          {:error, :no_static_analysis_found}
        end
    end
  end

  defp get_args(argv) do
    {opts, _args, invalid} =
      OptionParser.parse(
        argv,
        strict: [include_deps: :keep]
      )

    case invalid do
      [] ->
        deps_filter = Keyword.get_values(opts, :include_deps)

        validate_options(deps_filter)

      _ ->
        {:error, :wrong_arguments}
    end
  end

  defp validate_options(deps_filter) do
    case Project.filter_deps(deps_filter) do
      [] ->
        {:validate_error, "Filter doesn't match any dependency"}

      _ ->
        [include_deps: deps_filter]
    end
  end

  defp print_help() do
    Mix.shell().info("""
    Usage: mix arch.collect.credo_issues [opts]

    opts: --include-deps 'deps_filter'

    Where `deps_filter` is a glob Unix-like pattern, matches dependencies name
    --include-deps can be used more than once.

    - `*` matches none or many tokens
    - `?` matches exactly one token
    - `[abc]` matches a set of tokens
    - `[a-z]` matches a range of tokens
    - `[!...]` matches anything but a set of tokens
    """)
  end
end