lib/ex_aws/dynamo/encoder.ex

defmodule ExAws.Dynamo.Encoder do
  @moduledoc """
  Takes an Elixir value and converts it into a Dynamo-style map.

  ```elixir
  MapSet.new [1,2,3] |> #{__MODULE__}.encode
  #=> %{"NS" => ["1", "2", "3"]}

  MapSet.new ["A","B","C"] |> #{__MODULE__}.encode
  #=> %{"SS" => ["A", "B", "C"]}

  "bubba" |> ExAws.Dynamo.Encoder.encode
  #=> %{"S" => "bubba"}
  ```

  This is handled via the ExAws.Dynamo.Encodable protocol.
  """

  alias ExAws.Dynamo.Encodable

  # These functions exist to ensure that encoding is idempotent.
  def encode(value), do: encode(value, [])
  def encode(%{"B" => _} = val, _), do: val
  def encode(%{"BOOL" => _} = val, _), do: val
  def encode(%{"BS" => _} = val, _), do: val
  def encode(%{"L" => _} = val, _), do: val
  def encode(%{"M" => _} = val, _), do: val
  def encode(%{"NS" => _} = val, _), do: val
  def encode(%{"NULL" => _} = val, _), do: val
  def encode(%{"N" => _} = val, _), do: val
  def encode(%{"S" => _} = val, _), do: val
  def encode(%{"SS" => _} = val, _), do: val

  def encode(value, options), do: Encodable.encode(value, options)

  # Use this in case you want to encode something already in Dynamo format
  # for some reason I cannot fathom. If you find yourself using this, please open an issue
  # so I can find out why and better support this.
  def encode!(value, options \\ []) do
    Encodable.encode(value, options)
  end

  def encode_root(value, options \\ []) do
    case Encodable.encode(value, options) do
      %{"M" => value} -> value
      %{"L" => value} -> value
    end
  end

  def atom_to_dynamo_type(:blob), do: "B"
  def atom_to_dynamo_type(:boolean), do: "BOOL"
  def atom_to_dynamo_type(:blob_set), do: "BS"
  def atom_to_dynamo_type(:list), do: "L"
  def atom_to_dynamo_type(:map), do: "M"
  def atom_to_dynamo_type(:number_set), do: "NS"
  def atom_to_dynamo_type(:null), do: "NULL"
  def atom_to_dynamo_type(:number), do: "N"
  def atom_to_dynamo_type(:string), do: "S"
  def atom_to_dynamo_type(:string_set), do: "SS"

  def atom_to_dynamo_type(value) do
    raise ArgumentError, "Unknown dynamo type for value: #{inspect(value)}"
  end
end