Current section

Files

Jump to
xandra lib xandra transport.ex
Raw

lib/xandra/transport.ex

defmodule Xandra.Transport do
@moduledoc false
# Internal module to abstract away operations on transport sockets, independent from
# the transport (TCP or SSL).
@type options() :: [{atom(), term()} | atom()]
@type error_reason() :: :inet.posix() | term()
@type t() :: %__MODULE__{
module: :gen_tcp | :ssl,
options: options(),
socket: :gen_tcp.socket() | :ssl.sslsocket() | nil
}
@enforce_keys [:module, :options]
defstruct [:module, :options, :socket]
defguard is_data_message(transport, msg)
when is_tuple(msg) and tuple_size(msg) == 3 and elem(msg, 0) in [:tcp, :ssl] and
elem(msg, 1) == transport.socket
defguard is_closed_message(transport, msg)
when is_tuple(msg) and tuple_size(msg) == 2 and
elem(msg, 0) in [:tcp_closed, :ssl_closed] and
elem(msg, 1) == transport.socket
defguard is_error_message(transport, msg)
when is_tuple(msg) and tuple_size(msg) == 3 and
elem(msg, 0) in [:tcp_error, :ssl_error] and
elem(msg, 1) == transport.socket
@spec connect(t(), :inet.ip_address() | charlist(), :inet.port_number(), timeout()) ::
{:ok, t()} | {:error, error_reason()}
def connect(%__MODULE__{socket: nil} = transport, address, port, timeout) do
with {:ok, socket} <- transport.module.connect(address, port, transport.options, timeout) do
{:ok, %__MODULE__{transport | socket: socket}}
end
end
@spec send(t(), iodata()) :: :ok | {:error, error_reason()}
def send(%__MODULE__{socket: socket, module: module}, data) when not is_nil(socket) do
module.send(socket, data)
end
@spec recv(t(), integer(), timeout()) :: {:ok, binary()} | {:error, error_reason()}
def recv(%__MODULE__{socket: socket, module: module}, bytes, timeout)
when not is_nil(socket) and is_integer(bytes) do
module.recv(socket, bytes, timeout)
end
@spec setopts(t(), options()) :: :ok | {:error, error_reason}
def setopts(%__MODULE__{} = transport, options) when is_list(options) do
inet_mod(transport.module).setopts(transport.socket, options)
end
@spec address_and_port(t()) ::
{:ok, {:inet.ip_address(), :inet.port_number()}} | {:error, error_reason}
def address_and_port(%__MODULE__{socket: socket} = transport) when not is_nil(socket) do
inet_mod(transport.module).peername(socket)
end
@spec close(t()) :: t()
def close(%__MODULE__{} = transport) do
_ = transport.module.close(transport.socket)
%__MODULE__{transport | socket: nil}
end
defp inet_mod(:gen_tcp), do: :inet
defp inet_mod(:ssl), do: :ssl
end