ip/lib/enumerable/ip/prefix.ex
James Harton d4ab558f30 Implement prefix contains prefix test.
Also override credo's max line length because who works in 80x25
anymore?
2017-10-11 18:06:48 +13:00

76 lines
2.2 KiB
Elixir

defimpl Enumerable, for: IP.Prefix do
alias IP.{Prefix, Address}
use Bitwise
@moduledoc """
Implements `Enumerable` for `IP.Prefix`, allowing consumers to iterate
through all addresses in a prefix.
"""
@doc """
Returns the number of addresses within the `prefix`.
## Examples
iex> "192.0.2.128/25"
...> |> IP.Prefix.from_string!()
...> |> Enum.count()
128
iex> "2001:db8::/121"
...> |> IP.Prefix.from_string!()
...> |> Enum.count()
128
"""
@spec count(Prefix.t) :: {:ok, non_neg_integer} | {:error, module}
def count(prefix), do: {:ok, Prefix.space(prefix)}
@doc """
Returns whether an `address` is contained by the `prefix`.
## Examples
iex> IP.Prefix.from_string!("192.0.2.128/25")
...> |> Enum.member?(IP.Address.from_string!("192.0.2.250"))
true
"""
@spec member?(Prefix.t, Address.t) :: {:ok, boolean} | {:error, module}
def member?(prefix, %Address{} = address), do: {:ok, Prefix.contains_address?(prefix, address)}
@doc """
Allows the reduction of `prefix` into a colection of addresses.
## Examples
iex> IP.Prefix.from_string!("192.0.2.128/29")
...> |> Stream.filter(fn a -> rem(IP.Address.to_integer(a), 2) == 0 end)
...> |> Enum.map(fn a -> IP.Address.to_string(a) end)
["192.0.2.130", "192.0.2.132", "192.0.2.134"]
"""
@spec reduce(Prefix.t, Enumerable.acc, Enumerable.reducer) :: \
Enumerable.result
def reduce(_, {:halt, acc}, _fun), do: {:halted, acc}
def reduce({prefix, pos, last}, {:suspend, acc}, fun), do: {:suspended, acc, &reduce({prefix, pos, last}, &1, fun)}
def reduce(%Prefix{} = prefix, {:cont, acc}, fun) do
first = prefix
|> Prefix.first()
|> Address.to_integer()
last = prefix
|> Prefix.last()
|> Address.to_integer()
reduce({prefix, first, last}, {:cont, acc}, fun)
end
def reduce({%Prefix{address: %Address{version: version}} = prefix, pos, last}, {:cont, acc}, fun) do
case pos do
^last -> {:done, acc}
pos ->
pos = pos + 1
next = Address.from_integer!(pos, version)
reduce({prefix, pos, last}, fun.(next, acc), fun)
end
end
end