lib/archeometer/explore/static.ex

defmodule Archeometer.Explore.Static do
  @moduledoc """
  Module for analysing AST and collecting some useful information For now the
  schemas are hardcoded. There are

  - Modules
  - Definitions (functions, macros)

  Hopefully in the future a more extendible solution will be in place.
  """

  def process_module(full_ast, mod) do
    %{
      name: Archeometer.Util.Code.resolve_mod_name(full_ast, mod),
      num_lines: Archeometer.Util.Code.num_lines(mod)
    }
  end

  def process_def(full_ast, df) do
    {type, _meta, _children} = df

    with {:ok, {name, _meta, args}} <- Archeometer.Util.Code.get_decl(df) do
      {:ok,
       %{
         name: name,
         type: type,
         num_lines: Archeometer.Util.Code.num_lines(df),
         cc: Credo.Check.Refactor.CyclomaticComplexity.complexity_for(df),
         args: args,
         module: Archeometer.Util.Code.resolve_mod_name(full_ast, df)
       }}
    end
  end

  def module_stats_for(source_files, test_paths) when is_list(source_files),
    do: Enum.flat_map(source_files, &module_stats_for(&1, test_paths))

  def module_stats_for(source_file, test_paths) do
    path = source_file.filename

    ast = Credo.SourceFile.ast(source_file)

    is_test = Enum.any?(test_paths, &String.starts_with?(path <> "\/", &1))

    meta = %{
      path: path,
      is_test: is_test
    }

    ast
    |> Archeometer.Util.Code.collect_defs(:defmodule)
    |> Enum.map(&Map.merge(process_module(ast, &1), meta))
  end

  def def_stats_for(source_files, defs) when is_list(source_files),
    do: Enum.flat_map(source_files, &def_stats_for(&1, defs))

  def def_stats_for(source_file, defs) do
    ast = Credo.SourceFile.ast(source_file)

    ast
    |> Archeometer.Util.Code.collect_nodes(defs)
    |> Archeometer.Util.Code.collect_defs(defs)
    |> Enum.flat_map(fn d ->
      case process_def(ast, d) do
        {:ok, elem} -> [elem]
        {:error, _} -> []
      end
    end)
  end
end