defmodule Baby.Util do
require Logger
@moduledoc """
Utility functions for use across the codebase
"""
defp arrow(:in), do: "⇒"
defp arrow(:out), do: "⇐"
defp arrow(:both), do: "⇔"
@doc """
Standardised connection activity logging for the supplied state
`dir`: `:in`, `:out`, `:both`
`msg`: a protocol message type atom
`level`: log lvel atom (default: `:debug`)
"""
def connection_log(conn_info, dir, msg, level \\ :debug)
def connection_log(ci, d, msg, level) when is_atom(msg),
do: connection_log(ci, d, Atom.to_string(msg), level)
def connection_log(conn_info, dir, msg, level) do
Logger.log(level, Enum.join([tilde_peer(conn_info), arrow(dir), msg], " "))
end
@doc """
Logging of fatal errors takes connection info and the error
Returns `:error`
"""
def log_fatal(conn_info, error) do
Logger.log(:error, Enum.join([tilde_peer(conn_info), error], " "))
:error
end
defp tilde_peer(conn_info) do
case Map.fetch(conn_info, :short_peer) do
{:ok, them} -> them
:error -> "~unknown"
end
end
@doc """
Returns tuples of the endpoints of the widest continuous ranges
in a list of integers
iex> Baby.Util.range_points([8, 2, 5, 4, 3, 10, 11, 16, 17])
[{2, 5}, {8, 8}, {10, 11}, {16, 17}]
iex> Baby.Util.range_points([127])
[{127, 127}]
iex> Baby.Util.range_points([])
[]
"""
# Since the working part requires so many parameters and a
# distinct sorted list, we do some likely extra setup work here
# This would probably suck if the lists were too large
def range_points(list) do
list |> Enum.sort() |> Enum.uniq() |> range_points(nil, nil, [])
end
defp range_points([], nil, nil, acc), do: Enum.reverse(acc)
defp range_points([], final, first, acc),
do: range_points([], nil, nil, [{first, final} | acc])
defp range_points([n | rest], curr, first, acc) do
cond do
curr == nil -> range_points(rest, n, n, acc)
curr == n - 1 -> range_points(rest, n, first, acc)
true -> range_points(rest, n, n, [{first, curr} | acc])
end
end
@doc """
Convert a `{count, unit}` period into a number of milliseconds
## Examples
iex> Baby.Util.period_to_ms({1, :hour})
3_600_000
iex> Baby.Util.period_to_ms({17, :minute})
1_020_000
"""
@spec period_to_ms({integer, atom}) :: integer | :error
def period_to_ms(period)
def period_to_ms({amt, :millisecond}), do: amt
def period_to_ms({amt, :second}), do: period_to_ms({amt * 1000, :millisecond})
def period_to_ms({amt, :minute}), do: period_to_ms({amt * 60, :second})
def period_to_ms({amt, :hour}), do: period_to_ms({amt * 60, :minute})
def period_to_ms({amt, :day}), do: period_to_ms({amt * 24, :hour})
def period_to_ms({amt, :week}), do: period_to_ms({amt * 7, :day})
def period_to_ms(_), do: :error
@doc """
A simple wrapper to convert a host binary into an IP tuple
"""
def host_to_ip(host) when is_binary(host) do
where = to_charlist(host)
case :inet.gethostbyname(where) do
{:ok, {:hostent, _, _, _, _, [addr | _more]}} ->
addr
_ ->
case :inet.parse_ipv6_address(where) do
{:ok, addr} -> addr
_ -> :error
end
end
end
def host_to_ip(_), do: :error
end