Current section

Files

Jump to
redix lib redix start_options.ex
Raw

lib/redix/start_options.ex

defmodule Redix.StartOptions do
@moduledoc false
@default_sentinel_options [
timeout: 500,
socket_opts: [],
ssl: false,
role: :primary
]
@default_options [
socket_opts: [],
ssl: false,
sync_connect: false,
backoff_initial: 500,
backoff_max: 30000,
exit_on_disconnection: false,
timeout: 5000
]
@allowed_options [
:host,
:port,
:database,
:password,
:name,
:sentinel,
:hibernate_after,
:debug,
:spawn_opt
] ++
Keyword.keys(@default_options)
def sanitize(options) when is_list(options) do
@default_options
|> Keyword.merge(options)
|> assert_only_known_options()
|> maybe_sanitize_sentinel_opts()
|> maybe_sanitize_host_and_port()
end
defp assert_only_known_options(options) do
Enum.each(options, fn {key, _value} ->
unless key in @allowed_options do
raise ArgumentError, "unknown option: #{inspect(key)}"
end
end)
options
end
defp maybe_sanitize_sentinel_opts(options) do
case Keyword.fetch(options, :sentinel) do
{:ok, sentinel_opts} ->
Keyword.put(options, :sentinel, sanitize_sentinel_opts(options, sentinel_opts))
:error ->
options
end
end
defp sanitize_sentinel_opts(opts, sentinel_opts) do
sentinel_opts =
case Keyword.fetch(sentinel_opts, :sentinels) do
{:ok, sentinels} when is_list(sentinels) and sentinels != [] ->
sentinels = Enum.map(sentinels, &normalize_sentinel_address(&1, sentinel_opts))
Keyword.replace!(sentinel_opts, :sentinels, sentinels)
{:ok, sentinels} ->
raise ArgumentError,
"the :sentinels option inside :sentinel must be a non-empty list, got: " <>
inspect(sentinels)
:error ->
raise ArgumentError, "the :sentinels option is required inside :sentinel"
end
unless Keyword.has_key?(sentinel_opts, :group) do
raise ArgumentError, "the :group option is required inside :sentinel"
end
if Keyword.has_key?(opts, :host) or Keyword.has_key?(opts, :port) do
raise ArgumentError, ":host or :port can't be passed as option if :sentinel is used"
end
Keyword.merge(@default_sentinel_options, sentinel_opts)
end
defp normalize_sentinel_address(sentinel_uri, sentinel_opts) when is_binary(sentinel_uri) do
sentinel_uri |> Redix.URI.opts_from_uri() |> normalize_sentinel_address(sentinel_opts)
end
defp normalize_sentinel_address(opts, sentinel_opts) when is_list(opts) do
opts =
if opts[:host] do
Keyword.update!(opts, :host, &to_charlist/1)
else
raise ArgumentError, "a host should be specified for each sentinel"
end
unless opts[:port] do
raise ArgumentError, "a port should be specified for each sentinel"
end
Keyword.merge(Keyword.take(sentinel_opts, [:password]), opts)
end
defp normalize_sentinel_address(other, _sentinel_opts) do
raise ArgumentError,
"sentinel address should be specified as a URI or a keyword list, got: " <>
inspect(other)
end
defp maybe_sanitize_host_and_port(options) do
if Keyword.has_key?(options, :sentinel) do
options
else
{host, port} =
case {Keyword.get(options, :host, "localhost"), Keyword.fetch(options, :port)} do
{{:local, _unix_socket_path}, {:ok, port}} when port != 0 ->
raise ArgumentError,
"when using Unix domain sockets, the port must be 0, got: #{inspect(port)}"
{{:local, _unix_socket_path} = host, :error} ->
{host, 0}
{_host, {:ok, port}} when not is_integer(port) ->
raise ArgumentError,
"expected an integer as the value of the :port option, got: #{inspect(port)}"
{host, {:ok, port}} when is_binary(host) ->
{String.to_charlist(host), port}
{host, :error} when is_binary(host) ->
{String.to_charlist(host), 6379}
end
Keyword.merge(options, host: host, port: port)
end
end
end