Skip to main content

lib/terminus_db/client/params.ex

defmodule TerminusDB.Client.Params do
  @moduledoc false

  # Internal helpers for building Req request parameters and bodies.
  #
  # There are two kinds of boolean-style query parameters in the TerminusDB API:
  #
  #   * Flag parameters -- their absence on the wire means `false`. Use
  #     `flag_param/2`, which omits `false` and `nil` so we don't clutter the
  #     query string with `=false` for defaults. Examples: `full_replace`,
  #     `raw_json`, `nuke`, `create`, `force`.
  #
  #   * Tri-state parameters -- the server defaults them to `true`, and `false`
  #     is a meaningful override that MUST be sent. Use `bool_param/2`, which
  #     sends any non-nil value (including `false`). Examples: `unfold`,
  #     `minimized`, `compress_ids`, `as_list`, `branches`, `verbose`,
  #     `expand_abstract`.
  #
  # `maybe_put/3` conditionally adds a key to a map body, skipping `nil` values
  # so omitted options don't appear in the JSON payload.

  @doc """
  Returns a single-element keyword list for a flag parameter, or `[]` when the
  value is falsy (`nil` or `false`). Use for parameters whose absence means
  `false` server-side.
  """
  @spec flag_param(atom(), term()) :: keyword()
  def flag_param(_name, nil), do: []
  def flag_param(_name, false), do: []
  def flag_param(name, true), do: [{name, true}]
  def flag_param(name, value), do: [{name, value}]

  @doc """
  Returns a single-element keyword list for a tri-state boolean parameter, or
  `[]` only when the value is `nil`. Use for parameters the server defaults to
  `true`, where an explicit `false` must be sent to override the default.
  """
  @spec bool_param(atom(), term()) :: keyword()
  def bool_param(_name, nil), do: []
  def bool_param(name, value), do: [{name, value}]

  @doc """
  Concatenates a list of `{name, value}` pairs (or `[]`s) into a single keyword
  list, dropping nil/false entries according to `flag_param/2` semantics.
  """
  @spec flags([{atom(), term()} | []]) :: keyword()
  def flags(pairs) do
    Enum.flat_map(pairs, fn
      [] -> []
      {name, value} -> flag_param(name, value)
    end)
  end

  @doc """
  Puts `key` into `map` only when `value` is not `nil`. Use to omit absent
  options from JSON request bodies.
  """
  @spec maybe_put(map(), term(), term()) :: map()
  def maybe_put(map, _key, nil), do: map
  def maybe_put(map, key, value), do: Map.put(map, key, value)
end