lib/ex_aliyun_ots/utils.ex

defmodule ExAliyunOts.Utils do
  @moduledoc """
  Common tools.
  """

  @geo_point_reg ~r/\-?\d+\.?\d*,\-?\d+\.?\d*/

  defmodule Guards do
    @moduledoc false

    defguard is_valid_primary_key_type(type) when type in [:integer, :binary, :string]

    defguard is_valid_string(value) when value != nil and value != "" and is_bitstring(value)

    defguard is_valid_table_ttl(value) when is_integer(value) and (value == -1 or value >= 86_400)

    defguard is_valid_input_columns(columns) when is_list(columns) or is_map(columns)
  end

  def valid_geo_point?(point) when is_bitstring(point) do
    Regex.match?(@geo_point_reg, point)
  end

  def row_to_map({pks, attrs}) when is_list(pks) and is_list(attrs) do
    %{}
    |> do_reduce_pks(pks)
    |> do_reduce_attrs(attrs)
  end

  def row_to_map({pks, nil}) when is_list(pks) do
    do_reduce_pks(%{}, pks)
  end

  def row_to_map({nil, attrs}) when is_list(attrs) do
    do_reduce_attrs(%{}, attrs)
  end

  def row_to_map(nil) do
    %{}
  end

  def row_to_map(row) do
    raise ExAliyunOts.RuntimeError, "Fail to transfer invalid row: #{inspect(row)} to map."
  end

  def attrs_to_row(attrs) when is_list(attrs) do
    Enum.reduce(attrs, [], &assemble_attribute_column/2)
  end

  def attrs_to_row(attrs) when is_map(attrs) do
    Enum.reduce(attrs, [], &assemble_attribute_column/2)
  end

  def attrs_to_row(attrs) do
    raise ExAliyunOts.RuntimeError,
          "Fail to transfer invalid attributes: #{inspect(attrs)} to row attributes column(s), expect it is a Map or Keyword."
  end

  defp do_reduce_pks(acc, items) do
    Enum.reduce(items, acc, fn {k, v}, acc ->
      Map.put(acc, String.to_atom(k), v)
    end)
  end

  defp do_reduce_attrs(acc, items) do
    Enum.reduce(items, acc, fn {k, v, _ts}, acc ->
      Map.put(acc, String.to_atom(k), v)
    end)
  end

  defp value_to_attribute_column(value) when is_map(value) or is_list(value) do
    Jason.encode!(value)
  end

  defp value_to_attribute_column(value) do
    value
  end

  defp assemble_attribute_column({key, value}, acc) when is_atom(key) do
    value = value_to_attribute_column(value)
    if value == nil, do: acc, else: acc ++ [{Atom.to_string(key), value}]
  end

  defp assemble_attribute_column({key, value}, acc) when is_bitstring(key) do
    value = value_to_attribute_column(value)
    if value == nil, do: acc, else: acc ++ [{key, value}]
  end

  defp assemble_attribute_column(_, acc) do
    acc
  end

  def downcase_atom(atom), do: atom |> to_string() |> String.downcase() |> String.to_atom()
end