lib/io/ansi/table/line_types.ex

defmodule IO.ANSI.Table.LineTypes do
  @moduledoc """
  Nests row line types in a reversed flat list of line `types`.
  """

  use PersistConfig

  alias IO.ANSI.Table.LineType

  @row_types get_env(:row_types)

  @doc """
  Nests row line types in a reversed flat list of line `types`.

  ## Examples

      iex> alias IO.ANSI.Table.LineTypes
      iex> types = Enum.reverse([:top, :header, :separator, :row, :bottom])
      iex> LineTypes.from(types)
      [:top, :header, :separator, [:row], :bottom]

      iex> alias IO.ANSI.Table.LineTypes
      iex> types = [:top, :header, :separator, :even_row, :odd_row, :bottom]
      iex> types = Enum.reverse(types)
      iex> LineTypes.from(types)
      [:top, :header, :separator, [:even_row, :odd_row], :bottom]

      iex> alias IO.ANSI.Table.LineTypes
      iex> types = Enum.reverse([:header, :separator, :row_1, :row_2, :row_3])
      iex> LineTypes.from(types)
      [:header, :separator, [:row_1, :row_2, :row_3]]

      iex> alias IO.ANSI.Table.LineTypes
      iex> types = Enum.reverse(
      ...>   [:header, :separator, :row, :separator, :row_1, :row_2, :row_3]
      ...> )
      iex> LineTypes.from(types)
      [:header, :separator, [:row], :separator, [:row_1, :row_2, :row_3]]
  """
  @spec from([LineType.non_row() | LineType.row()]) :: [LineType.t()]
  def from(types), do: Enum.reduce(types, [], &acc/2)

  ## Private functions

  @spec acc(LineType.non_row() | LineType.row(), [LineType.t()]) ::
          [LineType.t()]
  defp acc(type, [hd | tl]) when type in @row_types and is_list(hd) do
    [[type | hd] | tl]
  end

  defp acc(type, acc) when type in @row_types, do: [[type] | acc]
  defp acc(type, acc), do: [type | acc]
end