Skip to main content

lib/quack_db/error.ex

defmodule QuackDB.Error do
  @moduledoc """
  Structured error returned by the QuackDB client.
  """

  @type code :: atom()
  @type source :: :client | :server | :transport | :protocol

  @type t :: %__MODULE__{
          code: code(),
          message: String.t(),
          source: source(),
          retriable?: boolean(),
          query: iodata() | nil,
          connection_id: String.t() | nil,
          metadata: map()
        }

  defexception [:code, :message, :source, :retriable?, :query, :connection_id, :metadata]

  @impl true
  def exception(options) do
    new(
      Keyword.fetch!(options, :code),
      Keyword.fetch!(options, :message),
      Keyword.take(options, [:source, :retriable?, :query, :connection_id, :metadata])
    )
  end

  @spec new(code(), String.t(), Keyword.t()) :: t()
  def new(code, message, options \\ []) do
    %__MODULE__{
      code: code,
      message: message,
      source: Keyword.get(options, :source, :client),
      retriable?: Keyword.get(options, :retriable?, false),
      query: Keyword.get(options, :query),
      connection_id: Keyword.get(options, :connection_id),
      metadata: Keyword.get(options, :metadata, %{})
    }
  end

  @impl true
  def message(%__MODULE__{} = error) do
    [
      error.message,
      query_message(error.query),
      connection_message(error.connection_id)
    ]
    |> IO.iodata_to_binary()
  end

  defp query_message(nil), do: []
  defp query_message(query), do: ["\n\n    query: ", query]

  defp connection_message(nil), do: []
  defp connection_message(connection_id), do: ["\n    connection_id: ", connection_id]
end

defimpl Inspect, for: QuackDB.Error do
  import Inspect.Algebra

  alias QuackDB.Inspect, as: QuackInspect

  def inspect(error, opts) do
    fields = [
      code: error.code,
      source: error.source,
      message: QuackInspect.truncate(error.message),
      query: QuackInspect.truncate(error.query),
      connection_id: QuackInspect.short_id(error.connection_id),
      retriable?: error.retriable?
    ]

    concat(QuackInspect.container("QuackDB.Error", fields, opts))
  end
end