Update deps

This commit is contained in:
James Harton 2018-09-04 03:25:04 +00:00
parent 9047519d2f
commit 09ebfcf747
18 changed files with 337 additions and 396 deletions

View file

@ -1,145 +0,0 @@
# This file contains the configuration for Credo and you are probably reading
# this after creating it with `mix credo.gen.config`.
#
# If you find anything wrong or unclear in this file, please report an
# issue on GitHub: https://github.com/rrrene/credo/issues
#
%{
#
# You can have as many configs as you like in the `configs:` field.
configs: [
%{
#
# Run any exec using `mix credo -C <name>`. If no exec name is given
# "default" is used.
#
name: "default",
#
# These are the files included in the analysis:
files: %{
#
# You can give explicit globs or simply directories.
# In the latter case `**/*.{ex,exs}` will be used.
#
included: ["lib/", "src/", "web/", "apps/"],
excluded: [~r"/_build/", ~r"/deps/"]
},
#
# If you create your own checks, you must specify the source files for
# them here, so they can be loaded by Credo before running the analysis.
#
requires: [],
#
# If you want to enforce a style guide and need a more traditional linting
# experience, you can change `strict` to `true` below:
#
strict: false,
#
# If you want to use uncolored output by default, you can change `color`
# to `false` below:
#
color: true,
#
# You can customize the parameters of any check by adding a second element
# to the tuple.
#
# To disable a check put `false` as second element:
#
# {Credo.Check.Design.DuplicatedCode, false}
#
checks: [
{Credo.Check.Consistency.ExceptionNames},
{Credo.Check.Consistency.LineEndings},
{Credo.Check.Consistency.ParameterPatternMatching},
{Credo.Check.Consistency.SpaceAroundOperators},
{Credo.Check.Consistency.SpaceInParentheses},
{Credo.Check.Consistency.TabsOrSpaces},
# For some checks, like AliasUsage, you can only customize the priority
# Priority values are: `low, normal, high, higher`
#
{Credo.Check.Design.AliasUsage, priority: :low},
# For others you can set parameters
# If you don't want the `setup` and `test` macro calls in ExUnit tests
# or the `schema` macro in Ecto schemas to trigger DuplicatedCode, just
# set the `excluded_macros` parameter to `[:schema, :setup, :test]`.
#
{Credo.Check.Design.DuplicatedCode, excluded_macros: []},
# You can also customize the exit_status of each check.
# If you don't want TODO comments to cause `mix credo` to fail, just
# set this value to 0 (zero).
#
{Credo.Check.Design.TagTODO, exit_status: 2},
{Credo.Check.Design.TagFIXME},
{Credo.Check.Readability.FunctionNames},
{Credo.Check.Readability.LargeNumbers},
{Credo.Check.Readability.MaxLineLength, priority: :low, max_length: 120},
{Credo.Check.Readability.ModuleAttributeNames},
{Credo.Check.Readability.ModuleDoc},
{Credo.Check.Readability.ModuleNames},
{Credo.Check.Readability.ParenthesesOnZeroArityDefs},
{Credo.Check.Readability.ParenthesesInCondition},
{Credo.Check.Readability.PredicateFunctionNames},
{Credo.Check.Readability.PreferImplicitTry},
{Credo.Check.Readability.RedundantBlankLines},
{Credo.Check.Readability.StringSigils},
{Credo.Check.Readability.TrailingBlankLine},
{Credo.Check.Readability.TrailingWhiteSpace},
{Credo.Check.Readability.VariableNames},
{Credo.Check.Readability.Semicolons},
{Credo.Check.Readability.SpaceAfterCommas},
{Credo.Check.Refactor.DoubleBooleanNegation},
{Credo.Check.Refactor.CondStatements},
{Credo.Check.Refactor.CyclomaticComplexity},
{Credo.Check.Refactor.FunctionArity},
{Credo.Check.Refactor.LongQuoteBlocks},
{Credo.Check.Refactor.MatchInCondition},
{Credo.Check.Refactor.NegatedConditionsInUnless},
{Credo.Check.Refactor.NegatedConditionsWithElse},
{Credo.Check.Refactor.Nesting},
{Credo.Check.Refactor.PipeChainStart},
{Credo.Check.Refactor.UnlessWithElse},
{Credo.Check.Warning.BoolOperationOnSameValues},
{Credo.Check.Warning.IExPry},
{Credo.Check.Warning.IoInspect},
{Credo.Check.Warning.LazyLogging},
{Credo.Check.Warning.OperationOnSameValues},
{Credo.Check.Warning.OperationWithConstantResult},
{Credo.Check.Warning.UnusedEnumOperation},
{Credo.Check.Warning.UnusedFileOperation},
{Credo.Check.Warning.UnusedKeywordOperation},
{Credo.Check.Warning.UnusedListOperation},
{Credo.Check.Warning.UnusedPathOperation},
{Credo.Check.Warning.UnusedRegexOperation},
{Credo.Check.Warning.UnusedStringOperation},
{Credo.Check.Warning.UnusedTupleOperation},
{Credo.Check.Warning.RaiseInsideRescue},
# Controversial and experimental checks (opt-in, just remove `, false`)
#
{Credo.Check.Refactor.ABCSize, false},
{Credo.Check.Refactor.AppendSingleItem, false},
{Credo.Check.Refactor.VariableRebinding, false},
{Credo.Check.Warning.MapGetUnsafePass, false},
{Credo.Check.Consistency.MultiAliasImportRequireUse, false},
# Deprecated checks (these will be deleted after a grace period)
#
{Credo.Check.Readability.Specs, false},
{Credo.Check.Warning.NameRedeclarationByAssignment, false},
{Credo.Check.Warning.NameRedeclarationByCase, false},
{Credo.Check.Warning.NameRedeclarationByDef, false},
{Credo.Check.Warning.NameRedeclarationByFn, false},
# Custom checks can be created using `mix credo.gen.check`.
#
]
}
]
}

3
.formatter.exs Normal file
View file

@ -0,0 +1,3 @@
[
inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"]
]

View file

@ -1,4 +1,11 @@
image: elixir:1.5
image: elixir:latest
cache:
key: "$CI_JOB_NAME"
paths:
- deps
- _build
- /root/.mix
variables:
MIX_ENV: "test"
@ -11,8 +18,20 @@ before_script:
test:
script:
- mix test
- mix credo --strict
- mix inch --pedantic
after_script:
- mix inch.report
credo:
script:
- mix credo --strict
audit:
script:
- mix hex.audit
format:
script:
- mix format --check-formatted
# inch:
# script:
# - mix inch --pedantic

View file

@ -20,7 +20,7 @@ defimpl Enumerable, for: IP.Prefix do
...> |> Enum.count()
128
"""
@spec count(Prefix.t) :: {:ok, non_neg_integer} | {:error, module}
@spec count(Prefix.t()) :: {:ok, non_neg_integer} | {:error, module}
def count(prefix), do: {:ok, Prefix.space(prefix)}
@doc """
@ -32,7 +32,7 @@ defimpl Enumerable, for: IP.Prefix do
...> |> Enum.member?(~i(192.0.2.250))
true
"""
@spec member?(Prefix.t, Address.t) :: {:ok, boolean} | {:error, module}
@spec member?(Prefix.t(), Address.t()) :: {:ok, boolean} | {:error, module}
def member?(prefix, %Address{} = address), do: {:ok, Prefix.contains_address?(prefix, address)}
@doc """
@ -45,26 +45,35 @@ defimpl Enumerable, for: IP.Prefix do
...> |> 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)}
@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
first =
prefix
|> Prefix.first()
|> Address.to_integer()
last = prefix
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
def reduce(
{%Prefix{address: %Address{version: version}} = prefix, pos, last},
{:cont, acc},
fun
) do
case pos do
^last -> {:done, acc}
^last ->
{:done, acc}
pos ->
pos = pos + 1
next = Address.from_integer!(pos, version)

View file

@ -17,9 +17,9 @@ defimpl Inspect, for: IP.Address do
iex> ~i(2001:db8::1)
#IP.Address<2001:db8::1 DOCUMENTATION>
"""
@spec inspect(Address.t, list) :: binary
@spec inspect(Address.t(), list) :: binary
def inspect(address, _opts) do
scope = Scope.address_scope(address)
concat ["#IP.Address<#{address} #{scope}>"]
concat(["#IP.Address<#{address} #{scope}>"])
end
end

View file

@ -15,9 +15,9 @@ defimpl Inspect, for: IP.Prefix do
...> |> IP.Address.to_prefix(32)
#IP.Prefix<192.0.2.1/32 DOCUMENTATION>
"""
@spec inspect(Prefix.t, list) :: binary
@spec inspect(Prefix.t(), list) :: binary
def inspect(%Prefix{} = prefix, _opts) do
scope = Scope.prefix_scope(prefix)
concat ["#IP.Prefix<#{prefix} #{scope}>"]
concat(["#IP.Prefix<#{prefix} #{scope}>"])
end
end

View file

@ -1,6 +1,7 @@
defmodule IP.Address do
alias IP.{Address, Prefix}
alias IP.Address.{InvalidAddress, Helpers, ULA}
alias IP.Address
alias IP.Address.{Helpers, InvalidAddress, ULA}
alias IP.Prefix
defstruct ~w(address version)a
import Helpers
use Bitwise
@ -10,10 +11,10 @@ defmodule IP.Address do
"""
@typedoc "Valid IPv4 address - integer between zero and 32 ones."
@type ipv4 :: 0..0xffffffff
@type ipv4 :: 0..0xFFFFFFFF
@typedoc "Valid IPv6 address - integer between zero and 128 ones."
@type ipv6 :: 0..0xffffffffffffffffffffffffffffffff
@type ipv6 :: 0..0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
@typedoc "Valid IP address"
@type ip :: ipv4 | ipv6
@ -42,8 +43,12 @@ defmodule IP.Address do
{:error, "Unable to convert binary to address"}
"""
@spec from_binary(binary) :: {:ok, t} | {:error, term}
def from_binary(<<address :: unsigned-integer-size(32)>>), do: {:ok, %Address{address: address, version: 4}}
def from_binary(<<address :: unsigned-integer-size(128)>>), do: {:ok, %Address{address: address, version: 6}}
def from_binary(<<address::unsigned-integer-size(32)>>),
do: {:ok, %Address{address: address, version: 4}}
def from_binary(<<address::unsigned-integer-size(128)>>),
do: {:ok, %Address{address: address, version: 6}}
def from_binary(_address), do: {:error, "Unable to convert binary to address"}
@doc """
@ -92,7 +97,7 @@ defmodule IP.Address do
def from_integer(_address, 4), do: {:error, "Supplied address not within IPv4 address space"}
def from_integer(_address, 6), do: {:error, "Supplied address not within IPv6 address space"}
def from_integer(_address, version), do: {:error, "No such IP version #{inspect version}"}
def from_integer(_address, version), do: {:error, "No such IP version #{inspect(version)}"}
@doc """
Convert an integer into an IP address of specified version or raise an
@ -135,7 +140,9 @@ defmodule IP.Address do
@spec from_string(binary) :: {:ok, t} | {:error, term}
def from_string(address) when is_binary(address) do
case from_string(address, 6) do
{:ok, address} -> {:ok, address}
{:ok, address} ->
{:ok, address}
{:error, _} ->
case from_string(address, 4) do
{:ok, address} -> {:ok, address}
@ -183,28 +190,36 @@ defmodule IP.Address do
def from_string(address, 4) when is_binary(address) do
case :inet.parse_ipv4strict_address(String.to_charlist(address)) do
{:ok, addr} ->
addr = addr
addr =
addr
|> Tuple.to_list()
|> from_bytes()
{:ok, %Address{version: 4, address: addr}}
{:error, _} -> {:error, "Cannot parse IPv4 address"}
{:error, _} ->
{:error, "Cannot parse IPv4 address"}
end
end
def from_string(address, 6) when is_binary(address) do
case :inet.parse_ipv6strict_address(String.to_charlist(address)) do
{:ok, addr} ->
addr = addr
addr =
addr
|> Tuple.to_list()
|> from_bytes()
{:ok, %Address{version: 6, address: addr}}
{:error, _} -> {:error, "Cannot parse IPv6 address"}
{:error, _} ->
{:error, "Cannot parse IPv6 address"}
end
end
def from_string(_address, 4), do: {:error, "Cannot parse IPv4 address"}
def from_string(_address, 6), do: {:error, "Cannot parse IPv6 address"}
def from_string(_address, version), do: {:error, "No such IP version #{inspect version}"}
def from_string(_address, version), do: {:error, "No such IP version #{inspect(version)}"}
@doc """
Convert a string representation into an IP address of specified version or raise an
@ -243,24 +258,26 @@ defmodule IP.Address do
"""
@spec to_string(t) :: binary
def to_string(%Address{version: 4, address: addr}) do
a = addr >>> 0x18 &&& 0xff
b = addr >>> 0x10 &&& 0xff
c = addr >>> 0x08 &&& 0xff
d = addr &&& 0xff
a = addr >>> 0x18 &&& 0xFF
b = addr >>> 0x10 &&& 0xFF
c = addr >>> 0x08 &&& 0xFF
d = addr &&& 0xFF
{a, b, c, d}
|> :inet.ntoa()
|> List.to_string()
end
def to_string(%Address{version: 6, address: addr}) do
a = addr >>> 0x70 &&& 0xffff
b = addr >>> 0x60 &&& 0xffff
c = addr >>> 0x50 &&& 0xffff
d = addr >>> 0x40 &&& 0xffff
e = addr >>> 0x30 &&& 0xffff
f = addr >>> 0x20 &&& 0xffff
g = addr >>> 0x10 &&& 0xffff
h = addr &&& 0xffff
a = addr >>> 0x70 &&& 0xFFFF
b = addr >>> 0x60 &&& 0xFFFF
c = addr >>> 0x50 &&& 0xFFFF
d = addr >>> 0x40 &&& 0xFFFF
e = addr >>> 0x30 &&& 0xFFFF
f = addr >>> 0x20 &&& 0xFFFF
g = addr >>> 0x10 &&& 0xFFFF
h = addr &&& 0xFFFF
{a, b, c, d, e, f, g, h}
|> :inet.ntoa()
|> List.to_string()
@ -275,7 +292,7 @@ defmodule IP.Address do
...> |> IP.Address.to_prefix(32)
#IP.Prefix<192.0.2.1/32 DOCUMENTATION>
"""
@spec to_prefix(t, Prefix.prefix_length) :: Prefix.t
@spec to_prefix(t, Prefix.prefix_length()) :: Prefix.t()
def to_prefix(%Address{} = address, length), do: Prefix.new(address, length)
@doc """
@ -355,8 +372,8 @@ defmodule IP.Address do
"""
@spec eui_64?(t) :: boolean
def eui_64?(%Address{address: address, version: 6})
when (address &&& 0x20000fffe000000) == 0x20000fffe000000,
do: true
when (address &&& 0x20000FFFE000000) == 0x20000FFFE000000,
do: true
def eui_64?(_address), do: false
@ -371,16 +388,18 @@ defmodule IP.Address do
"""
@spec eui_64_mac(t) :: {:ok, binary} | {:error, term}
def eui_64_mac(%Address{address: address, version: 6})
when (address &&& 0x20000fffe000000) == 0x20000fffe000000
do
mac = address &&& 0xffffffffffffffff
when (address &&& 0x20000FFFE000000) == 0x20000FFFE000000 do
mac = address &&& 0xFFFFFFFFFFFFFFFF
head = mac >>> 40
tail = mac &&& 0xffffff
mac = ((head <<< 24) + tail) ^^^ 0x20000000000
<<a::binary-size(4), b::binary-size(4), c::binary-size(4)>> = mac
tail = mac &&& 0xFFFFFF
mac = ((head <<< 24) + tail) ^^^ 0x20000000000
<<a::binary-size(4), b::binary-size(4), c::binary-size(4)>> =
mac
|> Integer.to_string(16)
|> String.downcase()
|> String.pad_leading(12, "0")
{:ok, "#{a}.#{b}.#{c}"}
end
@ -418,7 +437,8 @@ defmodule IP.Address do
"""
@spec is_6to4?(t) :: boolean
def is_6to4?(%Address{address: address, version: 6})
when (address >>> 112) == 0x2002, do: true
when address >>> 112 == 0x2002,
do: true
def is_6to4?(_address), do: false
@ -438,9 +458,8 @@ defmodule IP.Address do
"""
@spec from_6to4(t) :: {:ok, t} | {:error, term}
def from_6to4(%Address{address: address, version: 6})
when (address >>> 112) == 0x2002
do
address = (address >>> 80) &&& 0xffffffff
when address >>> 112 == 0x2002 do
address = address >>> 80 &&& 0xFFFFFFFF
Address.from_integer(address, 4)
end
@ -457,7 +476,8 @@ defmodule IP.Address do
"""
@spec is_teredo?(t) :: boolean
def is_teredo?(%Address{address: address, version: 6})
when (address >>> 96) == 0x20010000, do: true
when address >>> 96 == 0x20010000,
do: true
def is_teredo?(_address), do: false
@ -483,13 +503,16 @@ defmodule IP.Address do
"""
@spec teredo(t) :: {:ok, map} | {:error, term}
def teredo(%Address{address: address, version: 6})
when (address >>> 96) == 0x20010000 do
server = address >>> 64 &&& ((1 <<< 32) - 1)
client = address &&& ((1 <<< 32) - 1) &&& ((1 <<< 32) - 1)
port = (address >>> 32) &&& ((1 <<< 16) - 1)
%{server: Address.from_integer!(server, 4),
when address >>> 96 == 0x20010000 do
server = address >>> 64 &&& (1 <<< 32) - 1
client = address &&& (1 <<< 32) - 1 &&& (1 <<< 32) - 1
port = address >>> 32 &&& (1 <<< 16) - 1
%{
server: Address.from_integer!(server, 4),
client: Address.from_integer!(client, 4),
port: port}
port: port
}
end
def teredo(_address), do: {:error, "Not a teredo address"}
@ -506,12 +529,10 @@ defmodule IP.Address do
iex> IP.Address.generate_ula("60:f8:1d:ad:d8:90")
#IP.Address<fd29:f1ef:86a1::>
"""
@spec generate_ula(binary, non_neg_integer, boolean) :: \
{:ok, t} | {:error, term}
@spec generate_ula(binary, non_neg_integer, boolean) :: {:ok, t} | {:error, term}
def generate_ula(mac, subnet_id \\ 0, locally_assigned \\ true) do
with {:ok, address} <- ULA.generate(mac, subnet_id, locally_assigned),
{:ok, address} <- from_integer(address, 6)
do
{:ok, address} <- from_integer(address, 6) do
{:ok, address}
end
end
@ -521,7 +542,7 @@ defmodule IP.Address do
end
defp from_bytes([a, b, c, d, e, f, g, h]) do
(a <<< 0x70) + (b <<< 0x60) + (c <<< 0x50) + (d <<< 0x40) +
(e <<< 0x30) + (f <<< 0x20) + (g <<< 0x10) + h
(a <<< 0x70) + (b <<< 0x60) + (c <<< 0x50) + (d <<< 0x40) + (e <<< 0x30) + (f <<< 0x20) +
(g <<< 0x10) + h
end
end

View file

@ -6,7 +6,7 @@ defmodule IP.Address.Helpers do
"""
defmacro valid_ipv4_integer?(n) do
quote do
is_integer(unquote(n)) and unquote(n) >= 0 and unquote(n) <= 0xffffffff
is_integer(unquote(n)) and unquote(n) >= 0 and unquote(n) <= 0xFFFFFFFF
end
end
@ -15,25 +15,24 @@ defmodule IP.Address.Helpers do
"""
defmacro valid_ipv6_integer?(n) do
quote do
is_integer(unquote(n))
and unquote(n) >= 0
and unquote(n) <= 0xffffffffffffffffffffffffffffffff
is_integer(unquote(n)) and unquote(n) >= 0 and
unquote(n) <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
end
end
@doc """
Guard clause macro for "4 or 6"
"""
defmacro valid_ip_version?(4), do: quote do: true
defmacro valid_ip_version?(6), do: quote do: true
defmacro valid_ip_version?(_), do: quote do: false
defmacro valid_ip_version?(4), do: quote(do: true)
defmacro valid_ip_version?(6), do: quote(do: true)
defmacro valid_ip_version?(_), do: quote(do: false)
@doc """
Guard clause macro for "between 0 and 0xff"
"""
defmacro valid_byte?(n) do
quote do
is_integer(unquote(n)) and unquote(n) >= 0 and unquote(n) <= 0xff
is_integer(unquote(n)) and unquote(n) >= 0 and unquote(n) <= 0xFF
end
end
end

View file

@ -8,51 +8,48 @@ defmodule IP.Address.ULA do
@doc """
Generates an IPv6 Unique Local Address
"""
@spec generate(binary, non_neg_integer, true | false) :: \
{:ok, Address.ipv6} | {:error, term}
@spec generate(binary, non_neg_integer, true | false) :: {:ok, Address.ipv6()} | {:error, term}
def generate(mac, subnet_id, locally_assigned)
when is_binary(mac)
and is_integer(subnet_id) and subnet_id >= 0 and subnet_id <= 0xffff
and is_boolean(locally_assigned)
do
when is_binary(mac) and is_integer(subnet_id) and subnet_id >= 0 and subnet_id <= 0xFFFF and
is_boolean(locally_assigned) do
with %DateTime{} = now <- DateTime.utc_now(),
{:ok, ntp_time} <- ntp_time(now),
{:ok, eui} <- EUI64.eui_portion(mac),
{:ok, digest} <- generate_digest(ntp_time, eui),
{:ok, global_id} <- last_40_bits_of_digest(digest),
{:ok, prefix} <- generate_address(locally_assigned,
subnet_id,
global_id)
do
{:ok, ntp_time} <- ntp_time(now),
{:ok, eui} <- EUI64.eui_portion(mac),
{:ok, digest} <- generate_digest(ntp_time, eui),
{:ok, global_id} <- last_40_bits_of_digest(digest),
{:ok, prefix} <-
generate_address(
locally_assigned,
subnet_id,
global_id
) do
{:ok, prefix}
end
end
defp ntp_time(%DateTime{} = time) do
seconds = DateTime.to_unix(time)
seconds = DateTime.to_unix(time)
{msec, _} = time.microsecond
{:ok, ((seconds + 0x83AA7E80) <<< 32) + msec}
end
defp generate_digest(ntp_time, eui) do
with key <- << ntp_time::unsigned-integer-size(64),
eui::unsigned-integer-size(64) >>,
with key <- <<ntp_time::unsigned-integer-size(64), eui::unsigned-integer-size(64)>>,
digest <- :crypto.hash(:sha, key),
digest <- :binary.decode_unsigned(digest)
do
digest <- :binary.decode_unsigned(digest) do
{:ok, digest}
end
end
defp last_40_bits_of_digest(digest) do
{:ok, digest &&& 0xffffffffff}
{:ok, digest &&& 0xFFFFFFFFFF}
end
defp generate_address(locally_assigned, subnet_id, global_id) do
address = (0xfc <<< 120) +
(local_assignment_bit(locally_assigned) <<< 120) +
(global_id <<< 80) +
((subnet_id &&& 0xffff) <<< 64)
address =
(0xFC <<< 120) + (local_assignment_bit(locally_assigned) <<< 120) + (global_id <<< 80) +
((subnet_id &&& 0xFFFF) <<< 64)
{:ok, address}
end

View file

@ -1,6 +1,6 @@
defmodule IP.Prefix do
alias IP.{Prefix, Address}
alias IP.Prefix.{Parser, InvalidPrefix, Helpers, EUI64}
alias IP.{Address, Prefix}
alias IP.Prefix.{EUI64, Helpers, InvalidPrefix, Parser}
defstruct ~w(address mask)a
use Bitwise
import Helpers
@ -9,8 +9,8 @@ defmodule IP.Prefix do
Defines an IP prefix, otherwise known as a subnet.
"""
@ipv4_mask 0xffffffff
@ipv6_mask 0xffffffffffffffffffffffffffffffff
@ipv4_mask 0xFFFFFFFF
@ipv6_mask 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
@typedoc "Valid IPv4 prefix lengths from 0 to 32."
@type ipv4_prefix_length :: 0..32
@ -22,7 +22,7 @@ defmodule IP.Prefix do
@type prefix_length :: ipv4_prefix_length | ipv6_prefix_length
@typedoc "The main prefix type, contains an address and a mask value."
@type t :: %Prefix{address: Address.t, mask: Address.ip}
@type t :: %Prefix{address: Address.t(), mask: Address.ip()}
@doc """
Create an IP prefix from an `IP.Address` and `length`.
@ -35,7 +35,7 @@ defmodule IP.Prefix do
iex> IP.Prefix.new(~i(2001:db8::1), 64)
#IP.Prefix<2001:db8::/64 DOCUMENTATION>
"""
@spec new(Address.t, prefix_length) :: t
@spec new(Address.t(), prefix_length) :: t
def new(%Address{address: address, version: 4}, length) when length >= 0 and length <= 32 do
mask = calculate_mask_from_length(length, 32)
%Prefix{address: Address.from_integer!(address, 4), mask: mask}
@ -90,7 +90,7 @@ defmodule IP.Prefix do
...> |> IP.Prefix.from_string(4)
{:error, "Error parsing IPv4 prefix"}
"""
@spec from_string(binary, Address.version) :: {:ok, t} | {:error, term}
@spec from_string(binary, Address.version()) :: {:ok, t} | {:error, term}
def from_string(prefix, version), do: Parser.parse(prefix, version)
@doc """
@ -133,7 +133,7 @@ defmodule IP.Prefix do
...> |> IP.Prefix.from_string!(4)
#IP.Prefix<192.0.2.0/24 DOCUMENTATION>
"""
@spec from_string!(binary, Address.version) :: t
@spec from_string!(binary, Address.version()) :: t
def from_string!(prefix, version) do
case from_string(prefix, version) do
{:ok, prefix} -> prefix
@ -164,14 +164,12 @@ defmodule IP.Prefix do
"""
@spec length(t, prefix_length) :: t
def length(%Prefix{address: %Address{version: 4}} = prefix, length)
when is_number(length) and length >= 0 and length <= 32
do
when is_number(length) and length >= 0 and length <= 32 do
%{prefix | mask: calculate_mask_from_length(length, 32)}
end
def length(%Prefix{address: %Address{version: 6}} = prefix, length)
when is_number(length) and length >= 0 and length <= 128
do
when is_number(length) and length >= 0 and length <= 128 do
%{prefix | mask: calculate_mask_from_length(length, 128)}
end
@ -184,7 +182,7 @@ defmodule IP.Prefix do
...> |> IP.Prefix.mask()
0b11111111111111111111111100000000
"""
@spec mask(t) :: Address.ip
@spec mask(t) :: Address.ip()
def mask(%Prefix{mask: mask}), do: mask
@doc """
@ -196,7 +194,7 @@ defmodule IP.Prefix do
...> |> IP.Prefix.subnet_mask()
#IP.Address<255.255.255.0 RESERVED>
"""
@spec subnet_mask(t) :: Address.t
@spec subnet_mask(t) :: Address.t()
def subnet_mask(%Prefix{mask: mask, address: %Address{version: 4}}) do
mask
|> Address.from_integer!(4)
@ -211,7 +209,7 @@ defmodule IP.Prefix do
...> |> IP.Prefix.wildcard_mask()
#IP.Address<0.0.0.255 CURRENT NETWORK>
"""
@spec wildcard_mask(t) :: Address.t
@spec wildcard_mask(t) :: Address.t()
def wildcard_mask(%Prefix{mask: mask, address: %Address{version: 4}}) do
mask
|> bnot()
@ -232,7 +230,7 @@ defmodule IP.Prefix do
...> |> IP.Prefix.first()
#IP.Address<2001:db8:: DOCUMENTATION>
"""
@spec first(t) :: Address.t
@spec first(t) :: Address.t()
def first(%Prefix{address: %Address{address: address, version: version}, mask: mask}) do
Address.from_integer!(lowest_address(address, mask), version)
end
@ -250,7 +248,7 @@ defmodule IP.Prefix do
...> |> IP.Prefix.last()
#IP.Address<2001:db8::ffff:ffff:ffff:ffff DOCUMENTATION>
"""
@spec last(t) :: Address.t
@spec last(t) :: Address.t()
def last(%Prefix{address: %Address{address: address, version: 4}, mask: mask}) do
Address.from_integer!(highest_address(address, mask, 4), 4)
end
@ -287,20 +285,20 @@ defmodule IP.Prefix do
true
"""
@spec contains_prefix?(t, Address.t) :: boolean
def contains_address?(%Prefix{address: %Address{address: addr0, version: 4}, mask: mask} = _prefix,
%Address{address: addr1, version: 4} = _address)
when lowest_address(addr0, mask) <= addr1
and highest_address(addr0, mask, 4) >= addr1
do
@spec contains_prefix?(t, Address.t()) :: boolean
def contains_address?(
%Prefix{address: %Address{address: addr0, version: 4}, mask: mask} = _prefix,
%Address{address: addr1, version: 4} = _address
)
when lowest_address(addr0, mask) <= addr1 and highest_address(addr0, mask, 4) >= addr1 do
true
end
def contains_address?(%Prefix{address: %Address{address: addr0, version: 6}, mask: mask} = _prefix,
%Address{address: addr1, version: 6} = _address)
when lowest_address(addr0, mask) <= addr1
and highest_address(addr0, mask, 6) >= addr1
do
def contains_address?(
%Prefix{address: %Address{address: addr0, version: 6}, mask: mask} = _prefix,
%Address{address: addr1, version: 6} = _address
)
when lowest_address(addr0, mask) <= addr1 and highest_address(addr0, mask, 6) >= addr1 do
true
end
@ -334,19 +332,21 @@ defmodule IP.Prefix do
"""
@spec contains_prefix?(t, t) :: boolean
def contains_prefix?(%Prefix{address: %Address{address: oaddr, version: 4}, mask: omask} = _outside,
%Prefix{address: %Address{address: iaddr, version: 4}, mask: imask} = _inside)
when lowest_address(oaddr, omask) <= lowest_address(iaddr, imask)
and highest_address(oaddr, omask, 4) >= highest_address(iaddr, imask, 4)
do
def contains_prefix?(
%Prefix{address: %Address{address: oaddr, version: 4}, mask: omask} = _outside,
%Prefix{address: %Address{address: iaddr, version: 4}, mask: imask} = _inside
)
when lowest_address(oaddr, omask) <= lowest_address(iaddr, imask) and
highest_address(oaddr, omask, 4) >= highest_address(iaddr, imask, 4) do
true
end
def contains_prefix?(%Prefix{address: %Address{address: oaddr, version: 6}, mask: omask} = _outside,
%Prefix{address: %Address{address: iaddr, version: 6}, mask: imask} = _inside)
when lowest_address(oaddr, omask) <= lowest_address(iaddr, imask)
and highest_address(oaddr, omask, 6) >= highest_address(iaddr, imask, 6)
do
def contains_prefix?(
%Prefix{address: %Address{address: oaddr, version: 6}, mask: omask} = _outside,
%Prefix{address: %Address{address: iaddr, version: 6}, mask: imask} = _inside
)
when lowest_address(oaddr, omask) <= lowest_address(iaddr, imask) and
highest_address(oaddr, omask, 6) >= highest_address(iaddr, imask, 6) do
true
end
@ -364,16 +364,16 @@ defmodule IP.Prefix do
...> |> inspect()
"{:ok, #IP.Address<2001:db8::62f8:1dff:fead:d890 DOCUMENTATION>}"
"""
@spec eui_64(t, binary) :: {:ok, Address.t} | {:error, term}
def eui_64(%Prefix{address: %Address{version: 6},
mask: 0xffffffffffffffff0000000000000000} = prefix, mac)
do
@spec eui_64(t, binary) :: {:ok, Address.t()} | {:error, term}
def eui_64(
%Prefix{address: %Address{version: 6}, mask: 0xFFFFFFFFFFFFFFFF0000000000000000} = prefix,
mac
) do
with {:ok, eui_portion} <- EUI64.eui_portion(mac),
address <- Prefix.first(prefix),
address <- Address.to_integer(address),
address <- address + eui_portion,
{:ok, address} <- Address.from_integer(address, 6)
do
address <- Prefix.first(prefix),
address <- Address.to_integer(address),
address <- address + eui_portion,
{:ok, address} <- Address.from_integer(address, 6) do
{:ok, address}
end
end
@ -389,7 +389,7 @@ defmodule IP.Prefix do
...> |> IP.Prefix.eui_64!("60:f8:1d:ad:d8:90")
#IP.Address<2001:db8::62f8:1dff:fead:d890 DOCUMENTATION>
"""
@spec eui_64!(t, binary) :: Address.t
@spec eui_64!(t, binary) :: Address.t()
def eui_64!(prefix, mac) do
case eui_64(prefix, mac) do
{:ok, address} -> address
@ -413,13 +413,13 @@ defmodule IP.Prefix do
@spec space(t) :: non_neg_integer
def space(%Prefix{address: %Address{address: address, version: 4}, mask: mask}) do
first = address &&& mask
last = first + (~~~mask &&& @ipv4_mask)
last = first + (~~~mask &&& @ipv4_mask)
last - first + 1
end
def space(%Prefix{address: %Address{address: address, version: 6}, mask: mask}) do
first = address &&& mask
last = first + (~~~mask &&& @ipv6_mask)
last = first + (~~~mask &&& @ipv6_mask)
last - first + 1
end
@ -438,8 +438,10 @@ defmodule IP.Prefix do
"""
@spec usable(t) :: non_neg_integer
def usable(%Prefix{address: %Address{version: 4}} = prefix) do
space = prefix
space =
prefix
|> Prefix.space()
space - 2
end

View file

@ -14,11 +14,10 @@ defmodule IP.Prefix.EUI64 do
"""
@spec eui_portion(binary) :: {:ok, non_neg_integer} | {:error, term}
def eui_portion(mac) do
with {:ok, mac} <- remove_non_digits(mac),
{:ok, mac} <- hex_to_int(mac),
with {:ok, mac} <- remove_non_digits(mac),
{:ok, mac} <- hex_to_int(mac),
{:ok, head, tail} <- split_mac(mac),
{:ok, eui} <- generate_eui(head, tail)
do
{:ok, eui} <- generate_eui(head, tail) do
{:ok, eui}
else
{:error, _} = e -> e
@ -29,14 +28,14 @@ defmodule IP.Prefix.EUI64 do
{:ok, Regex.replace(~r/[^0-9a-f]/i, mac, "")}
end
defp split_mac(mac) when is_integer(mac) and mac >= 0 and mac <= 0xffffffffffff do
defp split_mac(mac) when is_integer(mac) and mac >= 0 and mac <= 0xFFFFFFFFFFFF do
head = mac >>> 24
tail = mac &&& 0xffffff
tail = mac &&& 0xFFFFFF
{:ok, head, tail}
end
def generate_eui(head, tail) do
address = (head <<< 40) + (0xfffe <<< 24) + tail
address = (head <<< 40) + (0xFFFE <<< 24) + tail
address = address ^^^ 0x0200000000000000
{:ok, address}
end

View file

@ -5,8 +5,8 @@ defmodule IP.Prefix.Helpers do
@doc false
def calculate_mask_from_length(length, mask_length) do
pad = mask_length - length - 1
mask = n_times_reduce(length, 0, fn (i, mask) -> mask + (1 <<< i) end)
pad = mask_length - length - 1
mask = n_times_reduce(length, 0, fn i, mask -> mask + (1 <<< i) end)
mask <<< pad
end
@ -22,6 +22,7 @@ defmodule IP.Prefix.Helpers do
end
defp n_times_reduce(0, acc, _fun), do: acc
defp n_times_reduce(n, acc, fun) do
acc = fun.(n, acc)
n_times_reduce(n - 1, acc, fun)
@ -35,15 +36,14 @@ defmodule IP.Prefix.Helpers do
defmacro highest_address(addr, mask, 4) do
quote do
(unquote(addr) &&& unquote(mask)) +
(~~~unquote(mask) &&& 0xffffffff)
(unquote(addr) &&& unquote(mask)) + (~~~unquote(mask) &&& 0xFFFFFFFF)
end
end
defmacro highest_address(addr, mask, 6) do
quote do
(unquote(addr) &&& unquote(mask)) +
(~~~unquote(mask) &&& 0xffffffffffffffffffffffffffffffff)
(~~~unquote(mask) &&& 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
end
end
end

View file

@ -1,5 +1,5 @@
defmodule IP.Prefix.Parser do
alias IP.{Prefix, Address}
alias IP.{Address, Prefix}
import IP.Prefix.Helpers
@moduledoc false
@ -22,10 +22,12 @@ defmodule IP.Prefix.Parser do
...> |> inspect()
"{:ok, #IP.Prefix<2001:db8::/64 DOCUMENTATION>}"
"""
@spec parse(binary) :: {:ok, Prefix.t} | {:error, term}
@spec parse(binary) :: {:ok, Prefix.t()} | {:error, term}
def parse(prefix) do
case parse(prefix, 4) do
{:ok, prefix} -> {:ok, prefix}
{:ok, prefix} ->
{:ok, prefix}
_ ->
case parse(prefix, 6) do
{:ok, prefix} -> {:ok, prefix}
@ -49,12 +51,11 @@ defmodule IP.Prefix.Parser do
...> |> inspect()
"{:ok, #IP.Prefix<2001:db8::/64 DOCUMENTATION>}"
"""
@spec parse(binary, Address.version) :: {:ok, Prefix.t} | {:error, term}
@spec parse(binary, Address.version()) :: {:ok, Prefix.t()} | {:error, term}
def parse(prefix, 4 = _version) do
with {:ok, address, mask} <- ensure_contains_slash(prefix),
{:ok, address} <- Address.from_string(address, 4),
{:ok, mask} <- parse_v4_mask(mask)
do
{:ok, address} <- Address.from_string(address, 4),
{:ok, mask} <- parse_v4_mask(mask) do
{:ok, Prefix.new(address, mask)}
else
_ -> {:error, "Error parsing IPv4 prefix"}
@ -63,9 +64,8 @@ defmodule IP.Prefix.Parser do
def parse(prefix, 6 = _version) do
with {:ok, address, mask} <- ensure_contains_slash(prefix),
{:ok, address} <- Address.from_string(address, 6),
{:ok, mask} <- parse_v6_mask(mask)
do
{:ok, address} <- Address.from_string(address, 6),
{:ok, mask} <- parse_v6_mask(mask) do
{:ok, Prefix.new(address, mask)}
else
_ -> {:error, "Error parsing IPv6 prefix"}
@ -82,10 +82,13 @@ defmodule IP.Prefix.Parser do
defp parse_v4_mask(mask) do
case Address.from_string(mask, 4) do
{:ok, address} ->
mask = address
|> Address.to_integer()
|> calculate_length_from_mask()
mask =
address
|> Address.to_integer()
|> calculate_length_from_mask()
{:ok, mask}
_ ->
{:ok, String.to_integer(mask)}
end

View file

@ -1,5 +1,5 @@
defmodule IP.Scope do
alias IP.{Prefix, Address}
alias IP.{Address, Prefix}
use Bitwise
require IP.Prefix
import IP.Prefix.Helpers
@ -11,40 +11,40 @@ defmodule IP.Scope do
"""
@v4_scopes [
{"0.0.0.0/8", "CURRENT NETWORK"},
{"10.0.0.0/8", "RFC1918 PRIVATE"},
{"127.0.0.0/8", "LOOPBACK"},
{"168.254.0.0/16", "AUTOCONF PRIVATE"},
{"172.16.0.0/12", "RFC1918 PRIVATE"},
{"192.0.0.0/24", "RESERVED (IANA)"},
{"192.0.2.0/24", "DOCUMENTATION"},
{"192.88.99.0/24", "6to4 ANYCAST"},
{"192.168.0.0/16", "RFC1918 PRIVATE"},
{"198.18.0.0/15", "NETWORK BENCHMARK TESTS"},
{"198.51.100.0/24", "DOCUMENTATION"},
{"203.0.113.0/24", "DOCUMENTATION"},
{"239.0.0.0/8", "LOCAL MULTICAST"},
{"224.0.0.0/4", "GLOBAL MULTICAST"},
{"240.0.0.0/4", "RESERVED"},
{"0.0.0.0/8", "CURRENT NETWORK"},
{"10.0.0.0/8", "RFC1918 PRIVATE"},
{"127.0.0.0/8", "LOOPBACK"},
{"168.254.0.0/16", "AUTOCONF PRIVATE"},
{"172.16.0.0/12", "RFC1918 PRIVATE"},
{"192.0.0.0/24", "RESERVED (IANA)"},
{"192.0.2.0/24", "DOCUMENTATION"},
{"192.88.99.0/24", "6to4 ANYCAST"},
{"192.168.0.0/16", "RFC1918 PRIVATE"},
{"198.18.0.0/15", "NETWORK BENCHMARK TESTS"},
{"198.51.100.0/24", "DOCUMENTATION"},
{"203.0.113.0/24", "DOCUMENTATION"},
{"239.0.0.0/8", "LOCAL MULTICAST"},
{"224.0.0.0/4", "GLOBAL MULTICAST"},
{"240.0.0.0/4", "RESERVED"},
{"255.255.255.255/32", "GLOBAL BROADCAST"},
{"0.0.0.0/0", "GLOBAL UNICAST"}
{"0.0.0.0/0", "GLOBAL UNICAST"}
]
@v6_scopes [
{"2002::/16", "GLOBAL UNICAST (6to4)"},
{"2001::/32", "GLOBAL UNICAST (TEREDO)"},
{"2001:10::/28", "ORCHID"},
{"2001:db8::/32", "DOCUMENTATION"},
{"2000::/3", "GLOBAL UNICAST"},
{"::/128", "UNSPECIFIED ADDRESS"},
{"::1/128", "LINK LOCAL LOOPBACK"},
{"::ffff:0:0/96", "IPv4 MAPPED"},
{"::/96", "IPv4 TRANSITION (deprecated)"},
{"fc00::/7", "UNIQUE LOCAL UNICAST"},
{"fec0::/10", "SITE LOCAL (deprecated)"},
{"fe80::/10", "LINK LOCAL UNICAST"},
{"ff00::/8", "MULTICAST"},
{"::/0", "RESERVED"}
{"2002::/16", "GLOBAL UNICAST (6to4)"},
{"2001::/32", "GLOBAL UNICAST (TEREDO)"},
{"2001:10::/28", "ORCHID"},
{"2001:db8::/32", "DOCUMENTATION"},
{"2000::/3", "GLOBAL UNICAST"},
{"::/128", "UNSPECIFIED ADDRESS"},
{"::1/128", "LINK LOCAL LOOPBACK"},
{"::ffff:0:0/96", "IPv4 MAPPED"},
{"::/96", "IPv4 TRANSITION (deprecated)"},
{"fc00::/7", "UNIQUE LOCAL UNICAST"},
{"fec0::/10", "SITE LOCAL (deprecated)"},
{"fe80::/10", "LINK LOCAL UNICAST"},
{"ff00::/8", "MULTICAST"},
{"::/0", "RESERVED"}
]
@doc """
@ -60,26 +60,28 @@ defmodule IP.Scope do
...> |> IP.Scope.address_scope()
"DOCUMENTATION"
"""
@spec address_scope(Address.t) :: binary
@spec address_scope(Address.t()) :: binary
Enum.each(@v4_scopes, fn {prefix, description} ->
%Prefix{address: %Address{address: addr0}, mask: mask} = prefix
%Prefix{address: %Address{address: addr0}, mask: mask} =
prefix
|> Prefix.from_string!()
def address_scope(%Address{address: addr1, version: 4})
when lowest_address(unquote(addr0), unquote(mask)) <= addr1
and highest_address(unquote(addr0), unquote(mask), 4) >= addr1
do
when lowest_address(unquote(addr0), unquote(mask)) <= addr1 and
highest_address(unquote(addr0), unquote(mask), 4) >= addr1 do
unquote(description)
end
end)
Enum.each(@v6_scopes, fn {prefix, description} ->
%Prefix{address: %Address{address: addr0}, mask: mask} = prefix
%Prefix{address: %Address{address: addr0}, mask: mask} =
prefix
|> Prefix.from_string!()
def address_scope(%Address{address: addr1, version: 6})
when lowest_address(unquote(addr0), unquote(mask)) <= addr1
and highest_address(unquote(addr0), unquote(mask), 6) >= addr1
do
when lowest_address(unquote(addr0), unquote(mask)) <= addr1 and
highest_address(unquote(addr0), unquote(mask), 6) >= addr1 do
unquote(description)
end
end)
@ -97,26 +99,30 @@ defmodule IP.Scope do
...> |> IP.Scope.prefix_scope()
"DOCUMENTATION"
"""
@spec prefix_scope(Prefix.t) :: binary
@spec prefix_scope(Prefix.t()) :: binary
Enum.each(@v4_scopes, fn {prefix0, description} ->
%Prefix{address: %Address{address: addr0}, mask: mask0} = prefix0
%Prefix{address: %Address{address: addr0}, mask: mask0} =
prefix0
|> Prefix.from_string!()
def prefix_scope(%Prefix{address: %Address{address: addr1, version: 4}, mask: mask1})
when lowest_address(unquote(addr0), unquote(mask0)) <= lowest_address(addr1, mask1)
and highest_address(unquote(addr0), unquote(mask0), 4) >= highest_address(addr1, mask1, 4)
do
when lowest_address(unquote(addr0), unquote(mask0)) <= lowest_address(addr1, mask1) and
highest_address(unquote(addr0), unquote(mask0), 4) >=
highest_address(addr1, mask1, 4) do
unquote(description)
end
end)
Enum.each(@v6_scopes, fn {prefix0, description} ->
%Prefix{address: %Address{address: addr0}, mask: mask0} = prefix0
%Prefix{address: %Address{address: addr0}, mask: mask0} =
prefix0
|> Prefix.from_string!()
def prefix_scope(%Prefix{address: %Address{address: addr1, version: 6}, mask: mask1})
when lowest_address(unquote(addr0), unquote(mask0)) <= lowest_address(addr1, mask1)
and highest_address(unquote(addr0), unquote(mask0), 6) >= highest_address(addr1, mask1, 6)
do
when lowest_address(unquote(addr0), unquote(mask0)) <= lowest_address(addr1, mask1) and
highest_address(unquote(addr0), unquote(mask0), 6) >=
highest_address(addr1, mask1, 6) do
unquote(description)
end
end)

View file

@ -1,5 +1,5 @@
defmodule IP.Sigil do
alias IP.{Prefix, Address}
alias IP.{Address, Prefix}
alias IP.Sigil.InvalidValue
@moduledoc """
@ -67,36 +67,57 @@ defmodule IP.Sigil do
iex> IP.Sigil.sigil_i("2001:db8::/32", 's')
#IP.Prefix<2001:db8::/32 DOCUMENTATION>
"""
@spec sigil_i(binary, [non_neg_integer]) :: Prefix.t | Address.t
@spec sigil_i(binary, [non_neg_integer]) :: Prefix.t() | Address.t()
def sigil_i(value, '' = _options) do
case Prefix.from_string(value) do
{:ok, prefix} -> prefix
{:ok, prefix} ->
prefix
{:error, _} ->
case Address.from_string(value) do
{:ok, address} -> address
{:error, _} -> raise(InvalidValue, message: "Unable to parse #{inspect value} as an IP address or prefix.")
{:ok, address} ->
address
{:error, _} ->
raise(InvalidValue,
message: "Unable to parse #{inspect(value)} as an IP address or prefix."
)
end
end
end
def sigil_i(value, 'f' = _options) do
case Prefix.from_string(value, 4) do
{:ok, prefix} -> prefix
{:ok, prefix} ->
prefix
{:error, _} ->
case Address.from_string(value, 4) do
{:ok, address} -> address
{:error, _} -> raise(InvalidValue, message: "Unable to parse #{inspect value} as an IPv4 address or prefix.")
{:ok, address} ->
address
{:error, _} ->
raise(InvalidValue,
message: "Unable to parse #{inspect(value)} as an IPv4 address or prefix."
)
end
end
end
def sigil_i(value, 's' = _options) do
case Prefix.from_string(value, 6) do
{:ok, prefix} -> prefix
{:ok, prefix} ->
prefix
{:error, _} ->
case Address.from_string(value, 6) do
{:ok, address} -> address
{:error, _} -> raise(InvalidValue, message: "Unable to parse #{inspect value} as an IPv6 address or prefix.")
{:ok, address} ->
address
{:error, _} ->
raise(InvalidValue,
message: "Unable to parse #{inspect(value)} as an IPv6 address or prefix."
)
end
end
end

View file

@ -1,5 +1,6 @@
defimpl String.Chars, for: IP.Prefix do
alias IP.Prefix
@moduledoc """
Implements `String.Chars` for `IP.Prefix`.
"""
@ -15,7 +16,7 @@ defimpl String.Chars, for: IP.Prefix do
"192.0.2.1/32"
"""
def to_string(%Prefix{} = prefix) do
length = Prefix.length(prefix)
length = Prefix.length(prefix)
address = Prefix.first(prefix)
"#{address}/#{length}"
end

12
mix.exs
View file

@ -4,14 +4,14 @@ defmodule IP.Mixfile do
@description """
Represtations and tools for IP addresses and networks.
"""
@version "1.0.0"
@version "1.1.0"
def project do
[
app: :ip,
version: @version,
elixir: "~> 1.5",
start_permanent: Mix.env == :prod,
start_permanent: Mix.env() == :prod,
package: package(),
deps: deps(),
description: @description
@ -20,8 +20,8 @@ defmodule IP.Mixfile do
def package do
[
maintainers: [ "James Harton <james@automat.nz>" ],
licenses: [ "MIT" ],
maintainers: ["James Harton <james@automat.nz>"],
licenses: ["MIT"],
links: %{
"Source" => "https://gitlab.com/jimsy/ip"
}
@ -40,8 +40,8 @@ defmodule IP.Mixfile do
[
{:ex_doc, ">= 0.0.0", only: :dev},
{:earmark, ">= 0.0.0", only: :dev},
{:credo, "~> 0.6", only: ~w(dev test)a, runtime: false},
{:inch_ex, "~> 0.5", only: ~w(dev test)a, runtime: false},
{:credo, "~> 0.10", only: ~w(dev test)a, runtime: false},
{:inch_ex, "~> 1.0", only: ~w(dev test)a, runtime: false},
{:dialyxir, "~> 0.5", only: ~w(dev test)a, runtime: false}
]
end

View file

@ -1,7 +1,13 @@
%{"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], []},
"credo": {:hex, :credo, "0.8.7", "b1aad9cd3aa7acdbaea49765bfc9f1605dc4555023a037dc9ea7a70539615bc8", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, optional: false]}]},
%{
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
"credo": {:hex, :credo, "0.10.0", "66234a95effaf9067edb19fc5d0cd5c6b461ad841baac42467afed96c78e5e9e", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
"dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], [], "hexpm"},
"earmark": {:hex, :earmark, "1.2.3", "206eb2e2ac1a794aa5256f3982de7a76bf4579ff91cb28d0e17ea2c9491e46a4", [:mix], []},
"ex_doc": {:hex, :ex_doc, "0.17.1", "39f777415e769992e6732d9589dc5846ea587f01412241f4a774664c746affbb", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, optional: false]}]},
"inch_ex": {:hex, :inch_ex, "0.5.6", "418357418a553baa6d04eccd1b44171936817db61f4c0840112b420b8e378e67", [:mix], [{:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, optional: false]}]},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], []}}
"earmark": {:hex, :earmark, "1.2.6", "b6da42b3831458d3ecc57314dff3051b080b9b2be88c2e5aa41cd642a5b044ed", [:mix], [], "hexpm"},
"ex_doc": {:hex, :ex_doc, "0.19.1", "519bb9c19526ca51d326c060cb1778d4a9056b190086a8c6c115828eaccea6cf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.7", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"},
"inch_ex": {:hex, :inch_ex, "1.0.0", "18496a900ca4b7542a1ff1159e7f8be6c2012b74ca55ac70de5e805f14cdf939", [:mix], [{:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
"jason": {:hex, :jason, "1.1.1", "d3ccb840dfb06f2f90a6d335b536dd074db748b3e7f5b11ab61d239506585eb2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
"makeup": {:hex, :makeup, "0.5.1", "966c5c2296da272d42f1de178c1d135e432662eca795d6dc12e5e8787514edf7", [:mix], [{:nimble_parsec, "~> 0.2.2", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
"makeup_elixir": {:hex, :makeup_elixir, "0.8.0", "1204a2f5b4f181775a0e456154830524cf2207cf4f9112215c05e0b76e4eca8b", [:mix], [{:makeup, "~> 0.5.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 0.2.2", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
"nimble_parsec": {:hex, :nimble_parsec, "0.2.2", "d526b23bdceb04c7ad15b33c57c4526bf5f50aaa70c7c141b4b4624555c68259", [:mix], [], "hexpm"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
}