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