defmodule GraphQLDocument.Variable do
@moduledoc """
[Variables](http://spec.graphql.org/October2021/#sec-Language.Variables) are
defined in [VariableDefinitions](http://spec.graphql.org/October2021/#VariableDefinition)
alongside the
[OperationType](http://spec.graphql.org/October2021/#OperationType) at the
beginning of a GraphQL document.
Any Variable that is defined in a Document can be used as the value of an
Argument.
"""
alias GraphQLDocument.{Name, Type, Value}
@typedoc "Expresses the name of a Variable to be used as a Value (See `GraphQLDocument.Value`)"
@type t :: {:var, Name.t()}
@typedoc """
These are given in the `variables` key of the Operation options. (See
`t:GraphQLDocument.Operation.option/0`.)
This is not the _usage_ of the Variable (as the Value of an Argument) but
rather defining its Name and Type to be used in the rest of the Document.
### Examples
GraphQLDocument.query(
[...],
variables: [
yearOfBirth: Int,
myId: {Int, null: false},
status: {String, default: "active"},
days: [String],
daysOfWeek: {[String], default: ["Saturday", "Sunday"]}
]
)
"""
@type definition :: {Name.t(), Type.t() | {Type.t(), [option]}}
@typedoc """
Options that can be passed when defining a variable.
For the `default` option, pass any `t:GraphQLDocument.Value.t/0`.
See `t:definition/0` for examples.
"""
@type option :: {:default, Value.t()} | Type.option()
@doc """
Returns a Variable as iodata to be inserted into a Document.
### Examples
iex> render({:var, :expandedInfo})
...> |> IO.iodata_to_binary()
"$expandedInfo"
iex> render({:var, "username"})
...> |> IO.iodata_to_binary()
"$username"
iex> render({:var, ""})
...> |> IO.iodata_to_binary()
** (ArgumentError) [empty string] is not a valid GraphQL name
"""
def render({:var, var}) do
[
?$,
Name.render!(var)
]
end
@doc """
Returns Variable definitions as iodata to be inserted into a Document.
> #### Leading Space {: .neutral}
>
> If any definitions are given, the returned iodata includes a leading space
> so that the output can be inserted into a Document either way and generate
> valid GraphQL syntax.
### Examples
iex> render_definitions([])
...> |> IO.iodata_to_binary()
""
iex> render_definitions([myInt: Int, debug: Boolean])
...> |> IO.iodata_to_binary()
" ($myInt: Int, $debug: Boolean)"
iex> render_definitions(lat: Float, lng: Float)
...> |> IO.iodata_to_binary()
" ($lat: Float, $lng: Float)"
"""
@spec render_definitions(definition) :: iodata
def render_definitions(variables) when is_list(variables) do
if Enum.any?(variables) do
[
?\s,
?(,
variables |> Enum.map(&render_definition/1) |> Enum.intersperse(", "),
?)
]
else
""
end
end
defp render_definition(variable) do
{name, type, opts} =
case variable do
{name, {type, opts}} -> {name, type, opts}
{name, type} -> {name, type, []}
end
[
?$,
Name.render!(name),
?:,
?\s,
Type.render({type, opts}),
if default = Keyword.get(opts, :default) do
[" = ", Value.render(default)]
else
[]
end
]
end
end