lib/ecto_mysql_extras/output.ex

defmodule EctoMySQLExtras.Output do
  @moduledoc """
  Output MySQL queries to the requested format.

  Formats:
  * `raw`: The raw MySQL query result (%MyXQL.Result{})
  * `ascii`: The query result formatted to ASCII.
  """

  def format(:raw, _info, result), do: result

  if Code.ensure_loaded?(TableRex) do
    def format(:ascii, info, result) do
      names = Enum.map(info.columns, & &1.name)
      types = Enum.map(info.columns, & &1.type)

      rows =
        if result.rows == [] do
          [["No results", nil]]
        else
          Enum.map(result.rows, &parse_row(&1, types))
        end

      rows
      |> TableRex.quick_render!(names, info.title)
      |> IO.puts()
    end

    defp parse_row(list, types) do
      list
      |> Enum.zip(types)
      |> Enum.map(&format_value/1)
    end

    def format_value({integer, :bytes}) when is_integer(integer), do: format_bytes(integer)
    def format_value({string, :string}), do: String.replace(string, "\n", "")
    def format_value({other, _}), do: inspect(other)

    defp format_bytes(bytes) do
      cond do
        bytes >= memory_unit(:TB) -> format_bytes(bytes, :TB)
        bytes >= memory_unit(:GB) -> format_bytes(bytes, :GB)
        bytes >= memory_unit(:MB) -> format_bytes(bytes, :MB)
        bytes >= memory_unit(:KB) -> format_bytes(bytes, :KB)
        true -> format_bytes(bytes, :B)
      end
    end

    defp format_bytes(bytes, :B) when is_integer(bytes), do: "#{bytes} bytes"

    defp format_bytes(bytes, unit) when is_integer(bytes) do
      value = bytes / memory_unit(unit)
      "#{:erlang.float_to_binary(value, decimals: 1)} #{unit}"
    end

    defp memory_unit(:TB), do: 1024 * 1024 * 1024 * 1024
    defp memory_unit(:GB), do: 1024 * 1024 * 1024
    defp memory_unit(:MB), do: 1024 * 1024
    defp memory_unit(:KB), do: 1024
  else
    def format(:ascii, _info, _result) do
      IO.warn("""
      If you want to display query results in ASCII format you should add `table_rex` as a dependency.
      """)
    end
  end
end