lib/graphql_document/argument.ex

defmodule GraphQLDocument.Argument do
  @moduledoc """
  [Arguments](http://spec.graphql.org/October2021/#sec-Language.Arguments)
  are specified in a keyword list.

      [width: 100, height: 50]

  See `render/1` for examples of more complicated argument sets.
  """

  alias GraphQLDocument.{Name, Value}

  @typedoc "A GraphQL argument."
  @type t :: {Name.t(), Value.t()}

  @doc ~S'''
  Returns a list of Arguments as iodata to be inserted into a Document.

  Any valid GraphQL Value can be sent as the value of an Argument.
  (See `t:GraphQLDocument.Value.t/0`.)

  ### Examples

      iex> render(height: 100, width: 50)
      ...> |> IO.iodata_to_binary()
      "(height: 100, width: 50)"

      iex> render(name: nil, age: 50)
      ...> |> IO.iodata_to_binary()
      "(name: null, age: 50)"

      iex> render(name: "Joshua", city: "Montreal", friendsOfFriends: true)
      ...> |> IO.iodata_to_binary()
      "(name: \"Joshua\", city: \"Montreal\", friendsOfFriends: true)"

      iex> render(%{person: [
      ...>   coordinates: [
      ...>     lat: 123.45,
      ...>     lng: 678.90
      ...>   ]
      ...> ]})
      ...> |> IO.iodata_to_binary()
      "(person: {coordinates: {lat: 123.45, lng: 678.9}})"

      iex> render(coordinates: [
      ...>   lat: {:var, :myLat},
      ...>   lng: {:var, :myLng}
      ...> ])
      ...> |> IO.iodata_to_binary()
      "(coordinates: {lat: $myLat, lng: $myLng})"

      iex> render(ids: [1, 2, 3])
      ...> |> IO.iodata_to_binary()
      "(ids: [1, 2, 3])"

  '''
  @spec render([t]) :: iolist
  def render(args) do
    unless is_map(args) or is_list(args) do
      raise "Expected a keyword list or map for args, received: #{inspect(args)}"
    end

    if Enum.any?(args) do
      [
        ?(,
        args
        |> Enum.map(fn {key, value} ->
          [
            Name.render!(key),
            ?:,
            ?\s,
            Value.render(value)
          ]
        end)
        |> Enum.intersperse(", "),
        ?)
      ]
    else
      []
    end
  end
end