Initial commit
This commit is contained in:
commit
a32c7a5b74
|
@ -0,0 +1,4 @@
|
|||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
|
@ -0,0 +1,26 @@
|
|||
# The directory Mix will write compiled artifacts to.
|
||||
/_build/
|
||||
|
||||
# If you run "mix test --cover", coverage assets end up here.
|
||||
/cover/
|
||||
|
||||
# The directory Mix downloads your dependencies sources to.
|
||||
/deps/
|
||||
|
||||
# Where third-party dependencies like ExDoc output generated docs.
|
||||
/doc/
|
||||
|
||||
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||
/.fetch
|
||||
|
||||
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||
erl_crash.dump
|
||||
|
||||
# Also ignore archive artifacts (built via "mix archive.build").
|
||||
*.ez
|
||||
|
||||
# Ignore package tarball (built via "mix hex.build").
|
||||
iban_ex-*.tar
|
||||
|
||||
# Temporary files, for example, from tests.
|
||||
/tmp/
|
|
@ -0,0 +1 @@
|
|||
alias IbanEx.{Country, Formatter, Iban, Parser, Deserialize, Validator}
|
|
@ -0,0 +1,21 @@
|
|||
# IbanEx
|
||||
|
||||
Library for working with IBAN numbers (parsing, validating and checking and formatting)
|
||||
|
||||
## Installation
|
||||
|
||||
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
|
||||
by adding `iban_ex` to your list of dependencies in `mix.exs`:
|
||||
|
||||
```elixir
|
||||
def deps do
|
||||
[
|
||||
{:iban_ex, "~> 0.1.0"}
|
||||
]
|
||||
end
|
||||
```
|
||||
|
||||
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
|
||||
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
|
||||
be found at <https://hexdocs.pm/iban_ex>.
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
defmodule IbanEx do
|
||||
@moduledoc """
|
||||
Documentation for `IbanEx`.
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Hello world.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> IbanEx.hello()
|
||||
:world
|
||||
|
||||
"""
|
||||
def hello do
|
||||
:world
|
||||
end
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
defmodule IbanEx.Commons do
|
||||
@spec normalize(binary()) :: binary()
|
||||
def normalize(string) do
|
||||
string
|
||||
|> to_string()
|
||||
|> String.replace(~r/\s*/i, "")
|
||||
|> String.upcase()
|
||||
end
|
||||
|
||||
@spec normalize_and_slice(binary(), Range.t()) :: binary()
|
||||
def normalize_and_slice(string, range) do
|
||||
string
|
||||
|> normalize()
|
||||
|> String.slice(range)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,52 @@
|
|||
defmodule IbanEx.Country do
|
||||
import IbanEx.Commons, only: [normalize: 1]
|
||||
|
||||
@type country_code() :: <<_::16>> | atom()
|
||||
@type error_tuple() :: {:error, atom()}
|
||||
|
||||
@supported_countries %{
|
||||
"AT" => IbanEx.Country.AT,
|
||||
"BE" => IbanEx.Country.BE,
|
||||
"BG" => IbanEx.Country.BG,
|
||||
"CH" => IbanEx.Country.CH,
|
||||
"CY" => IbanEx.Country.CY,
|
||||
"CZ" => IbanEx.Country.CZ,
|
||||
"DE" => IbanEx.Country.DE,
|
||||
"DK" => IbanEx.Country.DK,
|
||||
"ES" => IbanEx.Country.ES,
|
||||
"EE" => IbanEx.Country.EE,
|
||||
"FR" => IbanEx.Country.FR,
|
||||
"FI" => IbanEx.Country.FI,
|
||||
"GB" => IbanEx.Country.GB,
|
||||
"HR" => IbanEx.Country.HR,
|
||||
"LT" => IbanEx.Country.LT,
|
||||
"LU" => IbanEx.Country.LU,
|
||||
"LV" => IbanEx.Country.LV,
|
||||
"MT" => IbanEx.Country.MT,
|
||||
"NL" => IbanEx.Country.NL,
|
||||
"PL" => IbanEx.Country.PL,
|
||||
"PT" => IbanEx.Country.PT,
|
||||
"UA" => IbanEx.Country.UA
|
||||
}
|
||||
|
||||
@supported_country_codes Map.keys(@supported_countries)
|
||||
|
||||
@spec supported_countries() :: map()
|
||||
defp supported_countries(), do: @supported_countries
|
||||
|
||||
@spec supported_country_codes() :: [country_code()] | []
|
||||
def supported_country_codes(), do: @supported_country_codes
|
||||
|
||||
@spec country_module(country_code) :: Module.t() | error_tuple()
|
||||
def country_module(country_code) when is_binary(country_code) or is_atom(country_code) do
|
||||
normalized_country_code = normalize(country_code)
|
||||
case is_country_code_supported?(normalized_country_code) do
|
||||
true -> supported_countries()[normalized_country_code]
|
||||
_ -> {:error, :unsupported_country_code}
|
||||
end
|
||||
end
|
||||
|
||||
@spec is_country_code_supported?(country_code()) :: boolean()
|
||||
def is_country_code_supported?(country_code) when is_binary(country_code) or is_atom(country_code),
|
||||
do: Enum.member?(@supported_country_codes, normalize(country_code))
|
||||
end
|
|
@ -0,0 +1,34 @@
|
|||
defmodule IbanEx.Country.AT do
|
||||
@moduledoc """
|
||||
Austria IBAN parsing rules
|
||||
"""
|
||||
|
||||
alias IbanEx.Iban
|
||||
|
||||
@behaviour IbanEx.Country.Template
|
||||
|
||||
@size 20
|
||||
|
||||
@rule ~r/^(?<bank_code>[0-9]{5})(?<account_number>[0-9]{11})$/i
|
||||
|
||||
@spec size() :: 20
|
||||
def size(), do: @size
|
||||
|
||||
@spec rule() :: Regex.t()
|
||||
def rule(), do: @rule
|
||||
|
||||
@spec to_s(Iban.t()) :: binary()
|
||||
@spec to_s(Iban.t(), binary()) :: binary()
|
||||
def to_s(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
account_number: account_number
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, account_number]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
defmodule IbanEx.Country.BE do
|
||||
@moduledoc """
|
||||
Belgium IBAN parsing rules
|
||||
"""
|
||||
|
||||
alias IbanEx.Iban
|
||||
|
||||
@behaviour IbanEx.Country.Template
|
||||
|
||||
@size 16
|
||||
|
||||
@rule ~r/^(?<bank_code>[0-9]{3})(?<account_number>[0-9]{7})(?<national_check>[0-9]{2})$/i
|
||||
|
||||
@spec size() :: 16
|
||||
def size(), do: @size
|
||||
|
||||
@spec rule() :: Regex.t()
|
||||
def rule(), do: @rule
|
||||
|
||||
@spec to_s(Iban.t()) :: binary()
|
||||
@spec to_s(Iban.t(), binary()) :: binary()
|
||||
def to_s(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
branch_code: _branch_code,
|
||||
national_check: national_check,
|
||||
account_number: account_number
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, account_number, national_check]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
defmodule IbanEx.Country.BG do
|
||||
@moduledoc """
|
||||
Bulgaria IBAN parsing rules
|
||||
"""
|
||||
|
||||
alias IbanEx.Iban
|
||||
|
||||
@behaviour IbanEx.Country.Template
|
||||
|
||||
@size 22
|
||||
|
||||
@rule ~r/^(?<bank_code>[A-Z]{4})(?<branch_code>[0-9]{4})(?<account_number>[0-9]{2}[0-9A-Z]{8})$/i
|
||||
|
||||
@spec size() :: 22
|
||||
def size(), do: @size
|
||||
|
||||
@spec rule() :: Regex.t()
|
||||
def rule(), do: @rule
|
||||
|
||||
@spec to_s(Iban.t()) :: binary()
|
||||
@spec to_s(Iban.t(), binary()) :: binary()
|
||||
def to_s(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
branch_code: branch_code,
|
||||
national_check: _national_check,
|
||||
account_number: account_number
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, branch_code, account_number]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,34 @@
|
|||
defmodule IbanEx.Country.CH do
|
||||
@moduledoc """
|
||||
Switzerland IBAN parsing rules
|
||||
"""
|
||||
|
||||
alias IbanEx.Iban
|
||||
|
||||
@behaviour IbanEx.Country.Template
|
||||
|
||||
@size 21
|
||||
|
||||
@rule ~r/^(?<bank_code>[0-9]{5})(?<account_number>[0-9A-Z]{12})$/i
|
||||
|
||||
@spec size() :: 21
|
||||
def size(), do: @size
|
||||
|
||||
@spec rule() :: Regex.t()
|
||||
def rule(), do: @rule
|
||||
|
||||
@spec to_s(Iban.t()) :: binary()
|
||||
@spec to_s(Iban.t(), binary()) :: binary()
|
||||
def to_s(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
account_number: account_number
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, account_number]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
defmodule IbanEx.Country.CY do
|
||||
@moduledoc """
|
||||
Cyprus IBAN parsing rules
|
||||
"""
|
||||
|
||||
alias IbanEx.Iban
|
||||
|
||||
@behaviour IbanEx.Country.Template
|
||||
|
||||
@size 28
|
||||
|
||||
@rule ~r/^(?<bank_code>[0-9]{3})(?<branch_code>[0-9]{5})(?<account_number>[0-9A-Z]{16})$/i
|
||||
|
||||
@spec size() :: 28
|
||||
def size(), do: @size
|
||||
|
||||
@spec rule() :: Regex.t()
|
||||
def rule(), do: @rule
|
||||
|
||||
@spec to_s(Iban.t()) :: binary()
|
||||
@spec to_s(Iban.t(), binary()) :: binary()
|
||||
def to_s(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
branch_code: branch_code,
|
||||
national_check: _national_check,
|
||||
account_number: account_number
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, branch_code, account_number]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
defmodule IbanEx.Country.CZ do
|
||||
@moduledoc """
|
||||
Czech Republic IBAN parsing rules
|
||||
"""
|
||||
|
||||
alias IbanEx.Iban
|
||||
|
||||
@behaviour IbanEx.Country.Template
|
||||
|
||||
@size 24
|
||||
|
||||
@rule ~r/^(?<bank_code>[0-9]{4})(?<account_number>[0-9]{16})$/i
|
||||
|
||||
@spec size() :: 24
|
||||
def size(), do: @size
|
||||
|
||||
@spec rule() :: Regex.t()
|
||||
def rule(), do: @rule
|
||||
|
||||
@spec to_s(Iban.t()) :: binary()
|
||||
@spec to_s(Iban.t(), binary()) :: binary()
|
||||
def to_s(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
branch_code: _branch_code,
|
||||
national_check: _national_check,
|
||||
account_number: account_number
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, account_number]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
defmodule IbanEx.Country.DE do
|
||||
@moduledoc """
|
||||
Germany IBAN parsing rules
|
||||
"""
|
||||
|
||||
alias IbanEx.Iban
|
||||
|
||||
@behaviour IbanEx.Country.Template
|
||||
|
||||
@size 22
|
||||
|
||||
@rule ~r/^(?<bank_code>[0-9]{8})(?<account_number>[0-9]{10})$/i
|
||||
|
||||
@spec size() :: 22
|
||||
def size(), do: @size
|
||||
|
||||
@spec rule() :: Regex.t()
|
||||
def rule(), do: @rule
|
||||
|
||||
@spec to_s(Iban.t()) :: binary()
|
||||
@spec to_s(Iban.t(), binary()) :: binary()
|
||||
def to_s(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
branch_code: _branch_code,
|
||||
national_check: _national_check,
|
||||
account_number: account_number
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, account_number]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
defmodule IbanEx.Country.DK do
|
||||
@moduledoc """
|
||||
Denmark IBAN parsing rules
|
||||
"""
|
||||
|
||||
alias IbanEx.Iban
|
||||
|
||||
@behaviour IbanEx.Country.Template
|
||||
|
||||
@size 18
|
||||
|
||||
@rule ~r/^(?<bank_code>[0-9]{4})(?<account_number>[0-9]{10})$/i
|
||||
|
||||
@spec size() :: 18
|
||||
def size(), do: @size
|
||||
|
||||
@spec rule() :: Regex.t()
|
||||
def rule(), do: @rule
|
||||
|
||||
@spec to_s(Iban.t()) :: binary()
|
||||
@spec to_s(Iban.t(), binary()) :: binary()
|
||||
def to_s(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
branch_code: _branch_code,
|
||||
national_check: _national_check,
|
||||
account_number: account_number
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, account_number]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
defmodule IbanEx.Country.EE do
|
||||
@moduledoc """
|
||||
Estonian IBAN parsing rules
|
||||
"""
|
||||
|
||||
alias IbanEx.Iban
|
||||
|
||||
@behaviour IbanEx.Country.Template
|
||||
|
||||
@size 20
|
||||
|
||||
@rule ~r/^(?<bank_code>[0-9]{2})(?<branch_code>[0-9]{2})(?<account_number>[0-9]{11})(?<national_check>[0-9]{1})$/i
|
||||
|
||||
@spec size() :: 20
|
||||
def size(), do: @size
|
||||
|
||||
@spec rule() :: Regex.t()
|
||||
def rule(), do: @rule
|
||||
|
||||
@spec to_s(Iban.t()) :: binary()
|
||||
@spec to_s(Iban.t(), binary()) :: binary()
|
||||
def to_s(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
branch_code: branch_code,
|
||||
national_check: national_check,
|
||||
account_number: account_number
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, branch_code, account_number, national_check]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
defmodule IbanEx.Country.ES do
|
||||
@moduledoc """
|
||||
Spain IBAN parsing rules
|
||||
"""
|
||||
|
||||
alias IbanEx.Iban
|
||||
|
||||
@behaviour IbanEx.Country.Template
|
||||
|
||||
@size 24
|
||||
|
||||
@rule ~r/^(?<bank_code>[0-9]{4})(?<branch_code>[0-9]{4})(?<national_check>[0-9]{2})(?<account_number>[0-9]{10})$/i
|
||||
|
||||
@spec size() :: 24
|
||||
def size(), do: @size
|
||||
|
||||
@spec rule() :: Regex.t()
|
||||
def rule(), do: @rule
|
||||
|
||||
@spec to_s(Iban.t()) :: binary()
|
||||
@spec to_s(Iban.t(), binary()) :: binary()
|
||||
def to_s(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
branch_code: branch_code,
|
||||
national_check: national_check,
|
||||
account_number: account_number
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, branch_code, national_check, account_number]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
defmodule IbanEx.Country.FI do
|
||||
@moduledoc """
|
||||
Finland IBAN parsing rules
|
||||
"""
|
||||
|
||||
alias IbanEx.Iban
|
||||
|
||||
@behaviour IbanEx.Country.Template
|
||||
|
||||
@size 18
|
||||
|
||||
@rule ~r/^(?<bank_code>[0-9]{6})(?<account_number>[0-9]{7})(?<national_check>[0-9]{1})$/i
|
||||
|
||||
@spec size() :: 18
|
||||
def size(), do: @size
|
||||
|
||||
@spec rule() :: Regex.t()
|
||||
def rule(), do: @rule
|
||||
|
||||
@spec to_s(Iban.t()) :: binary()
|
||||
@spec to_s(Iban.t(), binary()) :: binary()
|
||||
def to_s(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
branch_code: _branch_code,
|
||||
national_check: national_check,
|
||||
account_number: account_number
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, account_number, national_check]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
defmodule IbanEx.Country.FR do
|
||||
@moduledoc """
|
||||
France IBAN parsing rules
|
||||
"""
|
||||
|
||||
alias IbanEx.Iban
|
||||
|
||||
@behaviour IbanEx.Country.Template
|
||||
|
||||
@size 27
|
||||
|
||||
@rule ~r/^(?<bank_code>[0-9]{5})(?<branch_code>[0-9]{5})(?<account_number>[0-9A-Z]{11})(?<national_check>[0-9]{2})$/i
|
||||
|
||||
@spec size() :: 27
|
||||
def size(), do: @size
|
||||
|
||||
@spec rule() :: Regex.t()
|
||||
def rule(), do: @rule
|
||||
|
||||
@spec to_s(Iban.t()) :: binary()
|
||||
@spec to_s(Iban.t(), binary()) :: binary()
|
||||
def to_s(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
branch_code: branch_code,
|
||||
national_check: national_check,
|
||||
account_number: account_number
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, branch_code, account_number, national_check]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,35 @@
|
|||
defmodule IbanEx.Country.GB do
|
||||
@moduledoc """
|
||||
United Kingdom IBAN parsing rules
|
||||
"""
|
||||
|
||||
alias IbanEx.Iban
|
||||
|
||||
@behaviour IbanEx.Country.Template
|
||||
|
||||
@size 22
|
||||
|
||||
@rule ~r/^(?<bank_code>[A-Z]{4})(?<branch_code>[0-9]{6})(?<account_number>[0-9]{8})$/i
|
||||
|
||||
@spec size() :: 22
|
||||
def size(), do: @size
|
||||
|
||||
@spec rule() :: Regex.t()
|
||||
def rule(), do: @rule
|
||||
|
||||
@spec to_s(Iban.t()) :: binary()
|
||||
@spec to_s(Iban.t(), binary()) :: binary()
|
||||
def to_s(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
branch_code: branch_code,
|
||||
account_number: account_number
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, branch_code, account_number]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
defmodule IbanEx.Country.HR do
|
||||
@moduledoc """
|
||||
Croatia IBAN parsing rules
|
||||
"""
|
||||
|
||||
alias IbanEx.Iban
|
||||
|
||||
@behaviour IbanEx.Country.Template
|
||||
|
||||
@size 21
|
||||
|
||||
@rule ~r/^(?<bank_code>[0-9]{7})(?<account_number>[0-9]{10})$/i
|
||||
|
||||
@spec size() :: 21
|
||||
def size(), do: @size
|
||||
|
||||
@spec rule() :: Regex.t()
|
||||
def rule(), do: @rule
|
||||
|
||||
@spec to_s(Iban.t()) :: binary()
|
||||
@spec to_s(Iban.t(), binary()) :: binary()
|
||||
def to_s(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
branch_code: _branch_code,
|
||||
national_check: _national_check,
|
||||
account_number: account_number
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, account_number]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
defmodule IbanEx.Country.LT do
|
||||
@moduledoc """
|
||||
Lithuanian IBAN parsing rules
|
||||
"""
|
||||
|
||||
alias IbanEx.Iban
|
||||
|
||||
@behaviour IbanEx.Country.Template
|
||||
|
||||
@size 20
|
||||
|
||||
@rule ~r/^(?<bank_code>[0-9]{5})(?<account_number>[0-9]{11})$/i
|
||||
|
||||
@spec size() :: 20
|
||||
def size(), do: @size
|
||||
|
||||
@spec rule() :: Regex.t()
|
||||
def rule(), do: @rule
|
||||
|
||||
@spec to_s(Iban.t()) :: binary()
|
||||
@spec to_s(Iban.t(), binary()) :: binary()
|
||||
def to_s(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
branch_code: _branch_code,
|
||||
national_check: _national_check,
|
||||
account_number: account_number
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, account_number]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
defmodule IbanEx.Country.LU do
|
||||
@moduledoc """
|
||||
Luxembourg IBAN parsing rules
|
||||
"""
|
||||
|
||||
alias IbanEx.Iban
|
||||
|
||||
@behaviour IbanEx.Country.Template
|
||||
|
||||
@size 20
|
||||
|
||||
@rule ~r/^(?<bank_code>[0-9]{3})(?<account_number>[0-9A-Z]{13})$/i
|
||||
|
||||
@spec size() :: 20
|
||||
def size(), do: @size
|
||||
|
||||
@spec rule() :: Regex.t()
|
||||
def rule(), do: @rule
|
||||
|
||||
@spec to_s(Iban.t()) :: binary()
|
||||
@spec to_s(Iban.t(), binary()) :: binary()
|
||||
def to_s(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
branch_code: _branch_code,
|
||||
national_check: _national_check,
|
||||
account_number: account_number
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, account_number]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
defmodule IbanEx.Country.LV do
|
||||
@moduledoc """
|
||||
Latvian IBAN parsing rules
|
||||
"""
|
||||
|
||||
alias IbanEx.Iban
|
||||
|
||||
@behaviour IbanEx.Country.Template
|
||||
|
||||
@size 21
|
||||
|
||||
@rule ~r/^(?<bank_code>[A-Z]{4})(?<account_number>[0-9A-Z]{13})$/i
|
||||
|
||||
@spec size() :: 21
|
||||
def size(), do: @size
|
||||
|
||||
@spec rule() :: Regex.t()
|
||||
def rule(), do: @rule
|
||||
|
||||
@spec to_s(Iban.t()) :: binary()
|
||||
@spec to_s(Iban.t(), binary()) :: binary()
|
||||
def to_s(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
branch_code: _branch_code,
|
||||
national_check: _national_check,
|
||||
account_number: account_number
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, account_number]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
defmodule IbanEx.Country.MT do
|
||||
@moduledoc """
|
||||
Malta IBAN parsing rules
|
||||
"""
|
||||
|
||||
alias IbanEx.Iban
|
||||
|
||||
@behaviour IbanEx.Country.Template
|
||||
|
||||
@size 31
|
||||
|
||||
@rule ~r/^(?<bank_code>[A-Z]{4})(?<branch_code>[0-9]{5})(?<account_number>[0-9A-Z]{18})$/i
|
||||
|
||||
@spec size() :: 31
|
||||
def size(), do: @size
|
||||
|
||||
@spec rule() :: Regex.t()
|
||||
def rule(), do: @rule
|
||||
|
||||
@spec to_s(Iban.t()) :: binary()
|
||||
@spec to_s(Iban.t(), binary()) :: binary()
|
||||
def to_s(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
branch_code: branch_code,
|
||||
national_check: _national_check,
|
||||
account_number: account_number
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, branch_code, account_number]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
defmodule IbanEx.Country.NL do
|
||||
@moduledoc """
|
||||
Netherlands IBAN parsing rules
|
||||
"""
|
||||
|
||||
alias IbanEx.Iban
|
||||
|
||||
@behaviour IbanEx.Country.Template
|
||||
|
||||
@size 18
|
||||
|
||||
@rule ~r/^(?<bank_code>[A-Z]{4})(?<account_number>[0-9]{10})$/i
|
||||
|
||||
@spec size() :: 18
|
||||
def size(), do: @size
|
||||
|
||||
@spec rule() :: Regex.t()
|
||||
def rule(), do: @rule
|
||||
|
||||
@spec to_s(Iban.t()) :: binary()
|
||||
@spec to_s(Iban.t(), binary()) :: binary()
|
||||
def to_s(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
branch_code: _branch_code,
|
||||
national_check: _national_check,
|
||||
account_number: account_number
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, account_number]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
defmodule IbanEx.Country.PL do
|
||||
@moduledoc """
|
||||
Poland IBAN parsing rules
|
||||
"""
|
||||
|
||||
alias IbanEx.Iban
|
||||
|
||||
@behaviour IbanEx.Country.Template
|
||||
|
||||
@size 28
|
||||
|
||||
@rule ~r/^(?<bank_code>[0-9]{3})(?<branch_code>[0-9]{4})(?<national_check>[0-9]{1})(?<account_number>[0-9]{16})$/i
|
||||
|
||||
@spec size() :: 28
|
||||
def size(), do: @size
|
||||
|
||||
@spec rule() :: Regex.t()
|
||||
def rule(), do: @rule
|
||||
|
||||
@spec to_s(Iban.t()) :: binary()
|
||||
@spec to_s(Iban.t(), binary()) :: binary()
|
||||
def to_s(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
branch_code: branch_code,
|
||||
national_check: national_check,
|
||||
account_number: account_number
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, branch_code, national_check, account_number]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
defmodule IbanEx.Country.PT do
|
||||
@moduledoc """
|
||||
Portugal IBAN parsing rules
|
||||
"""
|
||||
|
||||
alias IbanEx.Iban
|
||||
|
||||
@behaviour IbanEx.Country.Template
|
||||
|
||||
@size 25
|
||||
|
||||
@rule ~r/^(?<bank_code>[0-9]{4})(?<branch_code>[0-9]{4})(?<account_number>[0-9]{11})(?<national_check>[0-9]{2})$/i
|
||||
|
||||
@spec size() :: 25
|
||||
def size(), do: @size
|
||||
|
||||
@spec rule() :: Regex.t()
|
||||
def rule(), do: @rule
|
||||
|
||||
@spec to_s(Iban.t()) :: binary()
|
||||
@spec to_s(Iban.t(), binary()) :: binary()
|
||||
def to_s(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
branch_code: branch_code,
|
||||
account_number: account_number,
|
||||
national_check: national_check
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, branch_code, account_number, national_check]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,12 @@
|
|||
defmodule IbanEx.Country.Template do
|
||||
alias IbanEx.Iban
|
||||
@type size() :: non_neg_integer()
|
||||
@type rule() :: Regex.t()
|
||||
@type country_code() :: <<_::16>> | atom()
|
||||
@type joiner() :: String.t()
|
||||
|
||||
@callback size() :: size()
|
||||
@callback rule() :: rule()
|
||||
@callback to_s(Iban.t(), joiner()) :: String.t()
|
||||
@callback to_s(Iban.t()) :: String.t()
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
defmodule IbanEx.Country.UA do
|
||||
@moduledoc """
|
||||
Ukrainian IBAN parsing rules
|
||||
"""
|
||||
|
||||
alias IbanEx.Iban
|
||||
|
||||
@behaviour IbanEx.Country.Template
|
||||
|
||||
@size 29
|
||||
|
||||
@rule ~r/^(?<bank_code>[0-9]{6})(?<account_number>[0-9A-Z]{19})$/i
|
||||
|
||||
@spec size() :: 29
|
||||
def size(), do: @size
|
||||
|
||||
@spec rule() :: Regex.t()
|
||||
def rule(), do: @rule
|
||||
|
||||
@spec to_s(Iban.t()) :: binary()
|
||||
@spec to_s(Iban.t(), binary()) :: binary()
|
||||
def to_s(
|
||||
%Iban{
|
||||
country_code: country_code,
|
||||
check_digits: check_digits,
|
||||
bank_code: bank_code,
|
||||
branch_code: _branch_code,
|
||||
national_check: _national_check,
|
||||
account_number: account_number
|
||||
} = _iban,
|
||||
joiner \\ " "
|
||||
) do
|
||||
[country_code, check_digits, bank_code, account_number]
|
||||
|> Enum.join(joiner)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,49 @@
|
|||
defprotocol IbanEx.Deserialize do
|
||||
@type iban_or_error() :: IbanEx.Iban.t() | {:error, :can_not_parse_map | atom()}
|
||||
@spec to_iban(t()) :: iban_or_error()
|
||||
def to_iban(value)
|
||||
end
|
||||
|
||||
defimpl IbanEx.Deserialize, for: [BitString, String] do
|
||||
alias IbanEx.{Parser, Error}
|
||||
@type iban_or_error() :: IbanEx.Iban.t() | {:error, atom()}
|
||||
@spec to_iban(String.t()) :: iban_or_error()
|
||||
@spec to_iban(binary()) :: IbanEx.Iban.t()
|
||||
def to_iban(string) do
|
||||
case Parser.parse(string) do
|
||||
{:ok, iban} -> iban
|
||||
{:error, error_code} -> {error_code, Error.message(error_code)}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defimpl IbanEx.Deserialize, for: Map do
|
||||
alias IbanEx.Iban
|
||||
@type iban_or_error() :: IbanEx.Iban.t() | {:error, :can_not_parse_map}
|
||||
|
||||
@spec to_iban(map()) :: iban_or_error()
|
||||
def to_iban(
|
||||
%{
|
||||
country_code: _country_code,
|
||||
check_digits: _check_sum_digits,
|
||||
bank_code: _bank_code,
|
||||
account_number: _account_number
|
||||
} = map
|
||||
) do
|
||||
struct(Iban, map)
|
||||
end
|
||||
|
||||
def to_iban(
|
||||
%{
|
||||
"country_code" => _country_code,
|
||||
"check_digits" => _check_sum_digits,
|
||||
"bank_code" => _bank_code,
|
||||
"account_number" => _account_number
|
||||
} = map
|
||||
) do
|
||||
atomized_map = for {key, val} <- map, into: %{}, do: {String.to_atom(key), val}
|
||||
to_iban(atomized_map)
|
||||
end
|
||||
|
||||
def to_iban(map) when is_map(map), do: {:error, :can_not_parse_map}
|
||||
end
|
|
@ -0,0 +1,33 @@
|
|||
defmodule IbanEx.Error do
|
||||
@moduledoc """
|
||||
|
||||
"""
|
||||
|
||||
@type error() ::
|
||||
:unsupported_country_code
|
||||
| :invalid_format
|
||||
| :invalid_length
|
||||
| :invalid_checksum
|
||||
| :can_not_parse_map
|
||||
| atom()
|
||||
@type errors() :: [error()]
|
||||
@errors [
|
||||
:unsupported_country_code,
|
||||
:invalid_format,
|
||||
:invalid_length,
|
||||
:invalid_checksum,
|
||||
:can_not_parse_map
|
||||
]
|
||||
|
||||
@messages [
|
||||
unsupported_country_code: "Unsupported country code",
|
||||
invalid_format: "IBAN violates required format",
|
||||
invalid_length: "IBAN violates the required length",
|
||||
invalid_checksum: "IBAN's checksum is invalid",
|
||||
can_not_parse_map: "Can't parse map to IBAN struct"
|
||||
]
|
||||
|
||||
@spec message(error()) :: String.t()
|
||||
def message(error) when error in @errors, do: @messages[error]
|
||||
def message(_error), do: "Undefined error"
|
||||
end
|
|
@ -0,0 +1,42 @@
|
|||
defmodule IbanEx.Formatter do
|
||||
alias IbanEx.Country
|
||||
import IbanEx.Commons, only: [normalize: 1]
|
||||
|
||||
@available_formats [:compact, :pretty, :splitted]
|
||||
|
||||
@type iban() :: IbanEx.Iban.t()
|
||||
@type available_format() :: :compact | :pretty | :splitted
|
||||
@type available_formats_list() :: [:compact | :pretty | :splitted ]
|
||||
|
||||
@spec available_formats() :: available_formats_list()
|
||||
def available_formats(), do: @available_formats
|
||||
|
||||
@spec pretty(IbanEx.Iban.t()) :: binary()
|
||||
def pretty(iban), do: format(iban, :pretty)
|
||||
|
||||
@spec compact(IbanEx.Iban.t()) :: binary()
|
||||
def compact(iban), do: format(iban, :compact)
|
||||
|
||||
@spec splitted(IbanEx.Iban.t()) :: binary()
|
||||
def splitted(iban), do: format(iban, :splitted)
|
||||
|
||||
@spec format(iban()) :: String.t()
|
||||
@spec format(iban(), available_format()) :: String.t()
|
||||
def format(iban, format \\ :compact)
|
||||
def format(iban, :compact),
|
||||
do: format(iban, :pretty) |> normalize()
|
||||
|
||||
def format(iban, :pretty) do
|
||||
country_module = Country.country_module(iban.country_code)
|
||||
country_module.to_s(iban)
|
||||
end
|
||||
|
||||
def format(iban, :splitted) do
|
||||
compact = format(iban, :compact)
|
||||
|
||||
~r/.{1,4}/
|
||||
|> Regex.scan(compact)
|
||||
|> List.flatten()
|
||||
|> Enum.join(" ")
|
||||
end
|
||||
end
|
|
@ -0,0 +1,29 @@
|
|||
defmodule IbanEx.Iban do
|
||||
alias IbanEx.Formatter
|
||||
alias IbanEx.{Serialize}
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
country_code: <<_::16>>,
|
||||
check_digits: String.t(),
|
||||
bank_code: String.t(),
|
||||
branch_code: String.t() | nil,
|
||||
national_check: String.t() | nil,
|
||||
account_number: String.t()
|
||||
}
|
||||
defstruct country_code: "UA", check_digits: nil, bank_code: nil, branch_code: nil, national_check: nil, account_number: nil
|
||||
|
||||
@spec to_map(IbanEx.Iban.t()) :: map()
|
||||
defdelegate to_map(iban), to: Serialize
|
||||
|
||||
@spec to_string(IbanEx.Iban.t()) :: binary()
|
||||
defdelegate to_string(iban), to: Serialize
|
||||
|
||||
@spec pretty(IbanEx.Iban.t()) :: binary()
|
||||
defdelegate pretty(iban), to: Formatter
|
||||
|
||||
@spec splitted(IbanEx.Iban.t()) :: binary()
|
||||
defdelegate splitted(iban), to: Formatter
|
||||
|
||||
@spec compact(IbanEx.Iban.t()) :: binary()
|
||||
defdelegate compact(iban), to: Formatter
|
||||
end
|
|
@ -0,0 +1,37 @@
|
|||
defmodule IbanEx.Parser do
|
||||
alias IbanEx.{Country, Iban, Validator}
|
||||
import IbanEx.Commons, only: [normalize_and_slice: 2]
|
||||
|
||||
@type iban_string() :: String.t()
|
||||
@type country_code_string() :: <<_::16>>
|
||||
@type check_digits_string() :: <<_::16>>
|
||||
@type iban_or_error() :: IbanEx.Iban.t() | {:error, atom()}
|
||||
|
||||
@spec parse({:ok, String.t()} | String.t()) :: iban_or_error()
|
||||
def parse({:ok, iban_string}), do: parse(iban_string)
|
||||
def parse(iban_string) do
|
||||
with {:ok, valid_iban} <- Validator.validate(iban_string) do
|
||||
iban_map = %{
|
||||
country_code: country_code(valid_iban),
|
||||
check_digits: check_digits(valid_iban),
|
||||
}
|
||||
|
||||
regex = Country.country_module(iban_map.country_code).rule()
|
||||
bban = bban(iban_string)
|
||||
bban_map = for {key, val} <- Regex.named_captures(regex, bban), into: %{}, do: {String.to_atom(key), val}
|
||||
|
||||
{:ok, struct(Iban, Map.merge(iban_map, bban_map))}
|
||||
else
|
||||
{:error, error_type} -> {:error, error_type}
|
||||
end
|
||||
end
|
||||
|
||||
@spec country_code(iban_string()) :: country_code_string()
|
||||
def country_code(iban_string), do: normalize_and_slice(iban_string, 0..1)
|
||||
|
||||
@spec check_digits(binary()) :: check_digits_string()
|
||||
def check_digits(iban_string), do: normalize_and_slice(iban_string, 2..3)
|
||||
|
||||
@spec bban(binary()) :: binary()
|
||||
def bban(iban_string), do: normalize_and_slice(iban_string, 4..-1//1)
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
defmodule IbanEx.Serialize do
|
||||
alias IbanEx.{Iban, Formatter}
|
||||
|
||||
@spec to_string(Iban.t()) :: String.t()
|
||||
def to_string(iban), do: Formatter.format(iban)
|
||||
|
||||
@spec to_map(Iban.t()) :: Map.t()
|
||||
def to_map(iban), do: Map.from_struct(iban)
|
||||
end
|
|
@ -0,0 +1,46 @@
|
|||
defmodule IbanEx.Validator.Replacements do
|
||||
@moduledoc """
|
||||
Replacements in IBANs for checksums calculations
|
||||
"""
|
||||
|
||||
import IbanEx.Commons, only: [normalize: 1]
|
||||
|
||||
@type symbol() :: <<_::8>>
|
||||
@type value() :: <<_::16>>
|
||||
@type replacements() :: %{symbol() => value()}
|
||||
|
||||
@replacements %{
|
||||
"A" => "10",
|
||||
"B" => "11",
|
||||
"C" => "12",
|
||||
"D" => "13",
|
||||
"E" => "14",
|
||||
"F" => "15",
|
||||
"G" => "16",
|
||||
"H" => "17",
|
||||
"I" => "18",
|
||||
"J" => "19",
|
||||
"K" => "20",
|
||||
"L" => "21",
|
||||
"M" => "22",
|
||||
"N" => "23",
|
||||
"O" => "24",
|
||||
"P" => "25",
|
||||
"Q" => "26",
|
||||
"R" => "27",
|
||||
"S" => "28",
|
||||
"T" => "29",
|
||||
"U" => "30",
|
||||
"V" => "31",
|
||||
"W" => "32",
|
||||
"X" => "33",
|
||||
"Y" => "34",
|
||||
"Z" => "35"
|
||||
}
|
||||
|
||||
@spec replacements() :: replacements()
|
||||
def replacements(), do: @replacements
|
||||
|
||||
@spec replace(symbol()) :: value()
|
||||
def replace(symbol), do: @replacements[normalize(symbol)] || normalize(symbol)
|
||||
end
|
|
@ -0,0 +1,95 @@
|
|||
defmodule IbanEx.Validator do
|
||||
alias IbanEx.{Country, Parser}
|
||||
alias IbanEx.Validator.Replacements
|
||||
import IbanEx.Commons, only: [normalize: 1]
|
||||
|
||||
@spec validate(String.t()) :: {:ok, String.t()} | {:error}
|
||||
def validate(iban) do
|
||||
cond do
|
||||
iban_violates_format?(iban) ->
|
||||
{:error, :invalid_format}
|
||||
|
||||
iban_unsupported_country?(iban) ->
|
||||
{:error, :unsupported_country_code}
|
||||
|
||||
iban_violates_length?(iban) ->
|
||||
{:error, :invalid_length}
|
||||
|
||||
iban_violates_country_rule?(iban) ->
|
||||
{:error, :invalid_format}
|
||||
|
||||
iban_violates_checksum?(iban) ->
|
||||
{:error, :invalid_checksum}
|
||||
|
||||
true ->
|
||||
{:ok, normalize(iban)}
|
||||
end
|
||||
end
|
||||
|
||||
@spec size(String.t()) :: non_neg_integer()
|
||||
defp size(iban) do
|
||||
iban
|
||||
|> normalize()
|
||||
|> String.length()
|
||||
end
|
||||
|
||||
# - Check whether a given IBAN violates the required format.
|
||||
@spec iban_violates_format?(String.t()) :: boolean
|
||||
defp iban_violates_format?(iban),
|
||||
do: Regex.match?(~r/[^A-Z0-9]/i, normalize(iban))
|
||||
|
||||
# - Check whether a given IBAN violates the supported countries.
|
||||
@spec iban_unsupported_country?(String.t()) :: boolean
|
||||
defp iban_unsupported_country?(iban) do
|
||||
supported? =
|
||||
iban
|
||||
|> Parser.country_code()
|
||||
|> Country.is_country_code_supported?()
|
||||
|
||||
!supported?
|
||||
end
|
||||
|
||||
# - Check whether a given IBAN violates the required length.
|
||||
@spec iban_violates_length?(String.t()) :: boolean
|
||||
defp iban_violates_length?(iban) do
|
||||
with country_code <- Parser.country_code(iban),
|
||||
country_module <- Country.country_module(country_code) do
|
||||
size(iban) != country_module.size()
|
||||
else
|
||||
{:error, _} -> true
|
||||
end
|
||||
end
|
||||
|
||||
# - Check whether a given IBAN violates the country rules.
|
||||
@spec iban_violates_country_rule?(String.t()) :: boolean
|
||||
defp iban_violates_country_rule?(iban) do
|
||||
with country_code <- Parser.country_code(iban),
|
||||
bban <- Parser.bban(iban),
|
||||
country_module <- Country.country_module(country_code),
|
||||
rule <- country_module.rule() do
|
||||
!Regex.match?(rule, bban)
|
||||
else
|
||||
{:error, _} -> true
|
||||
end
|
||||
end
|
||||
|
||||
# - Check whether a given IBAN violates the required checksum.
|
||||
@spec iban_violates_checksum?(String.t()) :: boolean
|
||||
defp iban_violates_checksum?(iban) do
|
||||
check_sum_base = Parser.bban(iban) <> Parser.country_code(iban) <> "00"
|
||||
|
||||
replacements = Replacements.replacements()
|
||||
|
||||
remainder =
|
||||
for(<<c <- check_sum_base>>, into: "", do: replacements[<<c>>] || <<c>>)
|
||||
|> String.to_integer()
|
||||
|> rem(97)
|
||||
|
||||
checksum =
|
||||
(98 - remainder)
|
||||
|> Integer.to_string()
|
||||
|> String.pad_leading(2, "0")
|
||||
|
||||
checksum !== Parser.check_digits(iban)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,81 @@
|
|||
defmodule IbanEx.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
@source_url "https://g.tulz.dev/negrienko/iban-ex"
|
||||
@version "0.1.0"
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :iban_ex,
|
||||
version: @version,
|
||||
elixir: "~> 1.16",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps(),
|
||||
description: description(),
|
||||
dialyzer: dialyzer(),
|
||||
docs: docs(),
|
||||
package: package()
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help compile.app" to learn about applications.
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger]
|
||||
]
|
||||
end
|
||||
|
||||
defp description() do
|
||||
"""
|
||||
Library for working with IBAN numbers (parsing, validating and checking and formatting)
|
||||
"""
|
||||
end
|
||||
|
||||
defp dialyzer() do
|
||||
[
|
||||
plt_add_apps: [:iban_ex]
|
||||
]
|
||||
end
|
||||
|
||||
defp package() do
|
||||
[
|
||||
maintainers: ["Danylo Negrienko"],
|
||||
licenses: ["Apache-2.0"],
|
||||
links: %{"Git Repository" => @source_url, "Author's Blog" => "https://negrienko.com"}
|
||||
]
|
||||
end
|
||||
|
||||
defp docs() do
|
||||
[
|
||||
main: "readme",
|
||||
name: "IbanEx",
|
||||
source_ref: "v#{@version}",
|
||||
canonical: "http://hexdocs.pm/iban_ex",
|
||||
source_url: @source_url,
|
||||
extras: ["README.md"]
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
{:bankster, "~> 0.4.0"},
|
||||
|
||||
# Checks
|
||||
{:lettuce, "~> 0.3.0", only: :dev},
|
||||
{:ex_check, "~> 0.14.0", only: ~w(dev test)a, runtime: false},
|
||||
{:credo, ">= 0.0.0", only: ~w(dev test)a, runtime: false},
|
||||
{:dialyxir, ">= 0.0.0", only: ~w(dev test)a, runtime: false},
|
||||
{:doctor, ">= 0.0.0", only: ~w(dev test)a, runtime: false},
|
||||
{:ex_doc, ">= 0.0.0", only: ~w(dev test)a, runtime: false},
|
||||
{:sobelow, ">= 0.0.0", only: ~w(dev test)a, runtime: false},
|
||||
{:mix_audit, ">= 0.0.0", only: ~w(dev test)a, runtime: false},
|
||||
{:esbuild, "~> 0.7.0", runtime: Mix.env() == :dev},
|
||||
{:observer_cli, "~> 1.7.4", only: :dev, runtime: false},
|
||||
{:elixir_sense, github: "elixir-lsp/elixir_sense", only: ~w(dev)a}
|
||||
|
||||
# {:dep_from_hexpm, "~> 0.3.0"},
|
||||
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
||||
]
|
||||
end
|
||||
end
|
|
@ -0,0 +1,28 @@
|
|||
%{
|
||||
"bankster": {:hex, :bankster, "0.4.0", "5e4f35ba574ec7ca9f85d303802ae4331b1fe58a9f75e6267256bfcbd69f20dc", [:mix], [], "hexpm", "814fd27e37ecad0b1bb33e57a49156444f9d0e25341c22e29e49f502964e590a"},
|
||||
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
|
||||
"castore": {:hex, :castore, "1.0.5", "9eeebb394cc9a0f3ae56b813459f990abb0a3dedee1be6b27fdb50301930502f", [:mix], [], "hexpm", "8d7c597c3e4a64c395980882d4bca3cebb8d74197c590dc272cfd3b6a6310578"},
|
||||
"credo": {:hex, :credo, "1.7.5", "643213503b1c766ec0496d828c90c424471ea54da77c8a168c725686377b9545", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "f799e9b5cd1891577d8c773d245668aa74a2fcd15eb277f51a0131690ebfb3fd"},
|
||||
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
|
||||
"dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"},
|
||||
"doctor": {:hex, :doctor, "0.21.0", "20ef89355c67778e206225fe74913e96141c4d001cb04efdeba1a2a9704f1ab5", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "a227831daa79784eb24cdeedfa403c46a4cb7d0eab0e31232ec654314447e4e0"},
|
||||
"earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
|
||||
"elixir_sense": {:git, "https://github.com/elixir-lsp/elixir_sense.git", "885a63fc917a3f3468ddcf1b0855efd77be39182", []},
|
||||
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
|
||||
"esbuild": {:hex, :esbuild, "0.7.1", "fa0947e8c3c3c2f86c9bf7e791a0a385007ccd42b86885e8e893bdb6631f5169", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "66661cdf70b1378ee4dc16573fcee67750b59761b2605a0207c267ab9d19f13c"},
|
||||
"ex_check": {:hex, :ex_check, "0.14.0", "d6fbe0bcc51cf38fea276f5bc2af0c9ae0a2bb059f602f8de88709421dae4f0e", [:mix], [], "hexpm", "8a602e98c66e6a4be3a639321f1f545292042f290f91fa942a285888c6868af0"},
|
||||
"ex_doc": {:hex, :ex_doc, "0.31.1", "8a2355ac42b1cc7b2379da9e40243f2670143721dd50748bf6c3b1184dae2089", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "3178c3a407c557d8343479e1ff117a96fd31bafe52a039079593fb0524ef61b0"},
|
||||
"file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"},
|
||||
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
|
||||
"lettuce": {:hex, :lettuce, "0.3.0", "823198f053714282f980acc68c7157b9c78c740910cb4f572a642e020417a850", [:mix], [], "hexpm", "a47479d94ac37460481133213f08c8283dabbe762f4f8f8028456500d1fca9c4"},
|
||||
"makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"},
|
||||
"makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"},
|
||||
"makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"},
|
||||
"mix_audit": {:hex, :mix_audit, "2.1.2", "6cd5c5e2edbc9298629c85347b39fb3210656e541153826efd0b2a63767f3395", [:make, :mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.9", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "68d2f06f96b9c445a23434c9d5f09682866a5b4e90f631829db1c64f140e795b"},
|
||||
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
|
||||
"observer_cli": {:hex, :observer_cli, "1.7.4", "3c1bfb6d91bf68f6a3d15f46ae20da0f7740d363ee5bc041191ce8722a6c4fae", [:mix, :rebar3], [{:recon, "~> 2.5.1", [hex: :recon, repo: "hexpm", optional: false]}], "hexpm", "50de6d95d814f447458bd5d72666a74624eddb0ef98bdcee61a0153aae0865ff"},
|
||||
"recon": {:hex, :recon, "2.5.4", "05dd52a119ee4059fa9daa1ab7ce81bc7a8161a2f12e9d42e9d551ffd2ba901c", [:mix, :rebar3], [], "hexpm", "e9ab01ac7fc8572e41eb59385efeb3fb0ff5bf02103816535bacaedf327d0263"},
|
||||
"sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"},
|
||||
"yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"},
|
||||
"yaml_elixir": {:hex, :yaml_elixir, "2.9.0", "9a256da867b37b8d2c1ffd5d9de373a4fda77a32a45b452f1708508ba7bbcb53", [:mix], [{:yamerl, "~> 0.10", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "0cb0e7d4c56f5e99a6253ed1a670ed0e39c13fc45a6da054033928607ac08dfc"},
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
defmodule IbanExTest do
|
||||
use ExUnit.Case
|
||||
doctest IbanEx
|
||||
|
||||
test "greets the world" do
|
||||
assert IbanEx.hello() == :world
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
ExUnit.start()
|
Loading…
Reference in New Issue