lib/archeometer/query/term/container.ex

defmodule Archeometer.Query.Term.Container do
  @moduledoc """
  Utility functions for dealing with "container" AST. That is, tuples, list,
  keyword list and maps.

  Some examples of valid containers

      ["regular", "lists"]

      [keyword: "lists"]

      {"simple", "tuples"}

      %{map: "values"}

  The only restriction for containers is that they can't be nested.
  """

  @doc """
  Wrapper to work with the AST of container terms (i.e. tuples, maps and lists).
  """
  def reduce(ast, init_value, fun)

  # why do 2-tuples have a different AST?
  def reduce({expr1, expr2}, init, fun),
    do: reduce({:{}, [], [expr1, expr2]}, init, fun)

  def reduce({:{}, _meta, args}, init, fun),
    do: Enum.reduce(args, init, fun)

  def reduce({:%{}, _meta, args}, init, fun) do
    if Keyword.keyword?(args) do
      Enum.reduce(args, init, fun)
    else
      {:error, {:invalid_keys, args}}
    end
  end

  def reduce(exprs, init, fun) when is_list(exprs),
    do: Enum.reduce(exprs, init, fun)

  def reduce(expr, init, fun),
    do: reduce({:{}, [], [expr]}, init, fun)
end