lib/hologram/commons/encoder.ex

defmodule Hologram.Commons.Encoder do
  alias Hologram.Compiler.{Context, JSEncoder, MapKeyEncoder, Opts}
  alias Hologram.Compiler.IR.{MapAccess, Variable}

  defmacro __using__(_) do
    quote do
      import Hologram.Commons.Encoder
    end
  end

  def encode_as_array(data, %Context{} = context, %Opts{} = opts) do
    Enum.map(data, &JSEncoder.encode(&1, context, opts))
    |> Enum.join(", ")
    |> wrap_with_array()
  end

  defp encode_expression(expr, idx, expr_count, context, opts) do
    return = if idx == expr_count - 1, do: "return ", else: ""
    "#{return}#{JSEncoder.encode(expr, context, opts)};"
  end

  def encode_expressions(body, context, opts, separator) do
    expr_count = Enum.count(body)

    Enum.with_index(body)
    |> Enum.map(fn {expr, idx} -> encode_expression(expr, idx, expr_count, context, opts) end)
    |> Enum.join(separator)
  end

  def encode_function_name(function_name) do
    to_string(function_name)
    |> String.replace("?", "$question")
    |> String.replace("!", "$bang")
  end

  def encode_map_data(data, %Context{} = context, %Opts{} = opts) do
    Enum.map(data, fn {k, v} ->
      "'#{MapKeyEncoder.encode(k, context, opts)}': #{JSEncoder.encode(v, context, opts)}"
    end)
    |> Enum.join(", ")
    |> wrap_with_object()
  end

  def encode_primitive_key(type, value) do
    "~#{type}[#{value}]"
  end

  def encode_primitive_type(type, value) do
    "{ type: '#{type}', value: #{value} }"
  end

  defp encode_var({name, {idx, path}}, context) do
    acc = "let #{name} = arguments[#{idx}]"

    value =
      Enum.reduce(path, acc, fn type, acc ->
        acc <>
          case type do
            %MapAccess{key: key} ->
              ".data['#{MapKeyEncoder.encode(key, context, %Opts{})}']"

            %Variable{} ->
              ""
          end
      end)

    "#{value};"
  end

  def encode_vars(bindings, context, separator) do
    Enum.map(bindings, &encode_var(&1, context))
    |> Enum.join(separator)
  end

  def wrap_with_array(data) do
    if data != "", do: "[ #{data} ]", else: "[]"
  end

  def wrap_with_object(data) do
    if data != "", do: "{ #{data} }", else: "{}"
  end
end