lib/mix/tasks/archeometer.report.html.ex

defmodule Mix.Tasks.Arch.Report.Html do
  @moduledoc """
  Mix Task to generate a static HTML report.

  Usage:

      mix arch.report.html [options]

  Options:

    * `--limit` - Restricts the maximum number of results displayed from the queries in the report, Default is 10.
    * `--db` - Database filename

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

   Report is generated at `reports/dev/static/html`.
  """
  @shortdoc "Generates a code analysis HTML report"

  require Logger
  require EEx
  use Mix.Task
  use Archeometer.Repo

  import Archeometer.Query
  import Archeometer.Reports.Utils

  @impl Mix.Task
  def run(argv) do
    case parse_args(argv) do
      {:ok, %{limit: limit, db_name: db_name}} ->
        cfg = %{
          modules: apps(db_name),
          root_page_def: &Archeometer.Reports.PageDefinition.Project.definition/1,
          module_page_def: &Archeometer.Reports.PageDefinition.Application.definition/1,
          renderer: Archeometer.Reports.Render.Html,
          db_name: db_name,
          limit: limit
        }

        Archeometer.Reports.Generator.generate(cfg)

        Mix.shell().info("HTML report ready at '#{report_path(:html)}'")

      {:error, error} ->
        Mix.shell().error("Error: #{inspect(error)}")
        print_help()
    end
  end

  defp apps(db_name) do
    Archeometer.Repo.all(
      from(a in Archeometer.Schema.Application,
        select: [name: a.name]
      ),
      [],
      db_name
    )
    |> Map.get(:rows)
    |> List.flatten()
  end

  defp parse_args(argv) do
    {opts, _data, invalid_switches} =
      OptionParser.parse(
        argv,
        strict: [
          limit: :integer,
          db: :string
        ]
      )

    case invalid_switches do
      [] ->
        limit = Keyword.get(opts, :limit, 10)
        db_name = Keyword.get(opts, :db, default_db_name())
        validate_options(limit, db_name)

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

  defp validate_options(limit, db_name) do
    cond do
      limit <= 0 ->
        {:error, :invalid_limit}

      not Archeometer.Repo.db_ready?(:full, db_name) ->
        {:error, "Database is not existent or doesn't have the required tables."}

      true ->
        {:ok, %{limit: limit, db_name: db_name}}
    end
  end

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

    opts: --limit 'integer' --db db_file_name
    """)
  end
end