defmodule Bolt.Sips.Internals.Error do @moduledoc false defexception [:message, :code, :connection_id, :function, :type] @type t :: %__MODULE__{ message: String.t(), code: nil | any(), connection_id: nil | integer(), function: atom(), type: atom() } @doc false # Produce a Bolt.Sips.Internals.Error depending on the context. @spec exception(any(), nil | port(), atom()) :: Bolt.Sips.Internals.Error.t() def exception(%{"message" => message, "code" => code}, pid, function) do %Bolt.Sips.Internals.Error{ message: message, code: code, connection_id: get_id(pid), function: function, type: :cypher_error } end def exception({:error, :closed}, pid, function) do %Bolt.Sips.Internals.Error{ message: "Port #{inspect(pid)} is closed", connection_id: get_id(pid), function: function, type: :connection_error } end def exception({:failure, %Bolt.Sips.Internals.Error{message: _message, code: _code} = err}, _pid, _function) do err end def exception(%Bolt.Sips.Internals.Error{} = err, _pid, _function), do: err def exception(message, pid, function) do %Bolt.Sips.Internals.Error{ message: message_for(function, message), connection_id: get_id(pid), function: function, type: :protocol_error } end @spec message_for(nil | atom(), any()) :: String.t() defp message_for(:handshake, "HTTP") do """ Handshake failed. The port expected a HTTP request. This happens when trying to Neo4J using the REST API Port (default: 7474) instead of the Bolt Port (default: 7687). """ end defp message_for(:handshake, bin) when is_binary(bin) do """ Handshake failed. Expected 01:00:00:00 as a result, received: #{inspect(bin, base: :hex)}. """ end defp message_for(:handshake, other) do """ Handshake failed. Expected 01:00:00:00 as a result, received: #{inspect(other)}. """ end defp message_for(nil, message) do """ Unknown failure: #{inspect(message)} """ end defp message_for(_function, {:error, error}) do case error |> :inet.format_error() |> to_string do "unknown POSIX error" -> to_string(error) other -> other end end defp message_for(_function, {:ignored, []}) do """ The session is in a failed state and ignores further messages. You need to `ACK_FAILURE` or `RESET` in order to send new messages. """ end defp message_for(function, message) do """ #{function}: Unknown failure: #{inspect(message)} """ end @spec get_id(any()) :: nil | integer() defp get_id({:sslsocket, {:gen_tcp, port, _tls, _unused_yet}, _pid}) do get_id(port) end defp get_id(port) when is_port(port) do case Port.info(port, :id) do {:id, id} -> id nil -> nil end end defp get_id(_), do: nil end