8 Commits

50 changed files with 1003 additions and 534 deletions

View File

@@ -12,58 +12,66 @@ In just a few letters and numbers, the IBAN captures all of the country, bank, a
### Successfull case to parse IBAN ### Successfull case to parse IBAN
Parse string with valid formatted IBAN from supported country ```elixir
iex> "FI2112345600000785" |> IbanEx.Parser.parse()
```elixir {:ok, %IbanEx.Iban{
{:ok, iban} = "FI2112345600000785" |> IbanEx.Parser.parse() country_code: "FI",
IO.inspect(iban) check_digits: "21",
IbanEx.Iban.pretty(iban) bank_code: "123456",
``` branch_code: nil,
national_check: "5",
#### Response account_number: "0000078"
}}
%IbanEx.Iban{ ```
country_code: "FI",
check_digits: "21",
bank_code: "123456",
branch_code: nil,
national_check: "5",
account_number: "0000078"
}
"FI 21 123456 0000078 5"
### Errors cases of IBAN parsing ### Errors cases of IBAN parsing
Parse strings with invalid formatted IBANs from unsupported and supported countries #### To check IBAN's country is supported
```elixir ```elixir
{:error, unsupported_country_code} = "AZ21NABZ00000000137010001944" |> IbanEx.Parser.parse() iex> {:error, unsupported_country_code} = IbanEx.Parser.parse("AZ21NABZ00000000137010001944")
IO.inspect(IbanEx.Error.message(unsupported_country_code), label: unsupported_country_code) {:error, :unsupported_country_code}
iex> IbanEx.Error.message(unsupported_country_code)
"Unsupported country code"
```
{:error, invalid_length_code} = "AT6119043002345732012" |> IbanEx.Parser.parse() #### Validate and check IBAN length
IO.inspect(IbanEx.Error.message(invalid_length_code), label: invalid_length_code)
{:error, invalid_checksum} = "AT621904300234573201" |> IbanEx.Parser.parse() ```elixir
IO.inspect(IbanEx.Error.message(invalid_checksum), label: invalid_checksum) iex> {:error, invalid_length} = IbanEx.Parser.parse("AT6119043002345732012")
``` {:error, :invalid_length}
iex> IbanEx.Error.message(invalid_length)
"IBAN violates the required length"
```
#### Response ```elixir
iex> {:error, length_to_long} = IbanEx.Validator.check_iban_length("AT6119043002345732012")
{:error, :length_to_long}
iex> IbanEx.Error.message(length_to_long)
"IBAN longer then required length"
iex> {:error, length_to_short} = IbanEx.Validator.check_iban_length("AT61190430023457320")
{:error, :length_to_short}
iex> IbanEx.Error.message(length_to_short)
"IBAN shorter then required length"
```
unsupported_country_code: "Unsupported country code" #### Validate IBAN checksum
invalid_length: "IBAN violates the required length"
invalid_checksum: "IBAN's checksum is invalid"
```elixir
iex> {:error, invalid_checksum} = IbanEx.Parser.parse("AT621904300234573201")
{:error, :invalid_checksum}
iex> IbanEx.Error.message(invalid_checksum)
"IBAN's checksum is invalid"
```
## Installation ## Installation
If [available in Hex](https://hex.pm/docs/publish), the package can be installed The package can be installed by adding `iban_ex` to your list of dependencies in `mix.exs`:
by adding `iban_ex` to your list of dependencies in `mix.exs`:
```elixir ```elixir
def deps do def deps do
[ [
{:iban_ex, "~> 0.1.0"} {:iban_ex, "~> 0.1.5"}
] ]
end end
``` ```
@@ -71,4 +79,3 @@ end
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) 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 and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at <https://hexdocs.pm/iban_ex>. be found at <https://hexdocs.pm/iban_ex>.

View File

@@ -1,4 +1,6 @@
defmodule IbanEx.Commons do defmodule IbanEx.Commons do
@moduledoc false
@spec normalize(binary()) :: binary() @spec normalize(binary()) :: binary()
def normalize(string) do def normalize(string) do
string string
@@ -12,5 +14,9 @@ def normalize_and_slice(string, range) do
string string
|> normalize() |> normalize()
|> String.slice(range) |> String.slice(range)
# |> case do
# "" -> nil
# result -> result
# end
end end
end end

View File

@@ -1,4 +1,6 @@
defmodule IbanEx.Country do defmodule IbanEx.Country do
@moduledoc false
import IbanEx.Commons, only: [normalize: 1] import IbanEx.Commons, only: [normalize: 1]
@type country_code() :: <<_::16>> | atom() @type country_code() :: <<_::16>> | atom()
@@ -13,23 +15,38 @@ defmodule IbanEx.Country do
"CZ" => IbanEx.Country.CZ, "CZ" => IbanEx.Country.CZ,
"DE" => IbanEx.Country.DE, "DE" => IbanEx.Country.DE,
"DK" => IbanEx.Country.DK, "DK" => IbanEx.Country.DK,
"ES" => IbanEx.Country.ES,
"EE" => IbanEx.Country.EE, "EE" => IbanEx.Country.EE,
"FR" => IbanEx.Country.FR, "ES" => IbanEx.Country.ES,
"FI" => IbanEx.Country.FI, "FI" => IbanEx.Country.FI,
"FR" => IbanEx.Country.FR,
"GB" => IbanEx.Country.GB, "GB" => IbanEx.Country.GB,
"GI" => IbanEx.Country.GI,
"GR" => IbanEx.Country.GR,
"HR" => IbanEx.Country.HR, "HR" => IbanEx.Country.HR,
"HU" => IbanEx.Country.HU,
"IE" => IbanEx.Country.IE,
"IT" => IbanEx.Country.IT,
"LI" => IbanEx.Country.LI,
"LT" => IbanEx.Country.LT, "LT" => IbanEx.Country.LT,
"LU" => IbanEx.Country.LU, "LU" => IbanEx.Country.LU,
"LV" => IbanEx.Country.LV, "LV" => IbanEx.Country.LV,
"MC" => IbanEx.Country.MC,
"MT" => IbanEx.Country.MT, "MT" => IbanEx.Country.MT,
"NL" => IbanEx.Country.NL, "NL" => IbanEx.Country.NL,
"NO" => IbanEx.Country.NO,
"PL" => IbanEx.Country.PL, "PL" => IbanEx.Country.PL,
"PT" => IbanEx.Country.PT, "PT" => IbanEx.Country.PT,
"UA" => IbanEx.Country.UA "RO" => IbanEx.Country.RO,
"SE" => IbanEx.Country.SE,
"SM" => IbanEx.Country.SM,
"SI" => IbanEx.Country.SI,
"SK" => IbanEx.Country.SK,
"UA" => IbanEx.Country.UA,
"VA" => IbanEx.Country.VA
} }
@supported_country_codes Map.keys(@supported_countries) @supported_country_codes Map.keys(@supported_countries)
@supported_country_modules Map.values(@supported_countries)
@spec supported_countries() :: map() @spec supported_countries() :: map()
defp supported_countries(), do: @supported_countries defp supported_countries(), do: @supported_countries
@@ -37,6 +54,9 @@ defp supported_countries(), do: @supported_countries
@spec supported_country_codes() :: [country_code()] | [] @spec supported_country_codes() :: [country_code()] | []
def supported_country_codes(), do: @supported_country_codes def supported_country_codes(), do: @supported_country_codes
@spec supported_country_modules() :: [module()] | []
def supported_country_modules(), do: @supported_country_modules
@spec country_module(country_code) :: Module.t() | error_tuple() @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 def country_module(country_code) when is_binary(country_code) or is_atom(country_code) do
normalized_country_code = normalize(country_code) normalized_country_code = normalize(country_code)

View File

@@ -1,34 +1,25 @@
defmodule IbanEx.Country.AT do defmodule IbanEx.Country.AT do
@moduledoc """ @moduledoc """
Austria IBAN parsing rules Austria IBAN parsing rules
## Examples
```elixir
iex> %IbanEx.Iban{
...> country_code: "AT",
...> check_digits: "61",
...> bank_code: "19043",
...> branch_code: nil,
...> national_check: nil,
...> account_number: "00234573201"
...> }
...> |> IbanEx.Country.AT.to_string()
"AT 61 19043 00234573201"
```
""" """
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@size 20 @size 20
@rule ~r/^(?<bank_code>[0-9]{5})(?<account_number>[0-9]{11})$/i @rule ~r/^(?<bank_code>[0-9]{5})(?<account_number>[0-9]{11})$/i
@spec size() :: 20 use IbanEx.Country.Template
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 end

View File

@@ -1,25 +1,32 @@
defmodule IbanEx.Country.BE do defmodule IbanEx.Country.BE do
@moduledoc """ @moduledoc """
Belgium IBAN parsing rules Belgium IBAN parsing rules
## Examples
```elixir
iex> %IbanEx.Iban{
...> country_code: "BE",
...> check_digits: "68",
...> bank_code: "539",
...> branch_code: nil,
...> national_check: "34",
...> account_number: "0075470"
...> }
...> |> IbanEx.Country.BE.to_string()
"BE 68 539 0075470 34"
```
""" """
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@size 16 @size 16
@rule ~r/^(?<bank_code>[0-9]{3})(?<account_number>[0-9]{7})(?<national_check>[0-9]{2})$/i @rule ~r/^(?<bank_code>[0-9]{3})(?<account_number>[0-9]{7})(?<national_check>[0-9]{2})$/i
@spec size() :: 16 use IbanEx.Country.Template
def size(), do: @size
@spec rule() :: Regex.t() @impl IbanEx.Country.Template
def rule(), do: @rule @spec to_string(Iban.t()) :: binary()
@spec to_string(Iban.t(), binary()) :: binary()
@spec to_s(Iban.t()) :: binary() def to_string(
@spec to_s(Iban.t(), binary()) :: binary()
def to_s(
%Iban{ %Iban{
country_code: country_code, country_code: country_code,
check_digits: check_digits, check_digits: check_digits,

View File

@@ -1,25 +1,34 @@
defmodule IbanEx.Country.BG do defmodule IbanEx.Country.BG do
@moduledoc """ @moduledoc """
Bulgaria IBAN parsing rules Bulgaria IBAN parsing rules
## Examples
```elixir
iex> %IbanEx.Iban{
...> country_code: "BG",
...> check_digits: "80",
...> bank_code: "BNBG",
...> branch_code: "9661",
...> national_check: nil,
...> account_number: "1020345678"
...> }
...> |> IbanEx.Country.BG.to_string()
"BG 80 BNBG 9661 1020345678"
```
""" """
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@size 22 @size 22
@rule ~r/^(?<bank_code>[A-Z]{4})(?<branch_code>[0-9]{4})(?<account_number>[0-9]{2}[0-9A-Z]{8})$/i @rule ~r/^(?<bank_code>[A-Z]{4})(?<branch_code>[0-9]{4})(?<account_number>[0-9]{2}[0-9A-Z]{8})$/i
use IbanEx.Country.Template
@spec size() :: 22 @spec size() :: 22
def size(), do: @size
@spec rule() :: Regex.t() @impl IbanEx.Country.Template
def rule(), do: @rule @spec to_string(Iban.t()) :: binary()
@spec to_string(Iban.t(), binary()) :: binary()
@spec to_s(Iban.t()) :: binary() def to_string(
@spec to_s(Iban.t(), binary()) :: binary()
def to_s(
%Iban{ %Iban{
country_code: country_code, country_code: country_code,
check_digits: check_digits, check_digits: check_digits,

View File

@@ -1,34 +1,17 @@
defmodule IbanEx.Country.CH do defmodule IbanEx.Country.CH do
@moduledoc """ @moduledoc """
Switzerland IBAN parsing rules Switzerland IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "CH", check_digits: "93", bank_code: "00762", branch_code: nil, national_check: nil, account_number: "011623852957"}
iex> |> IbanEx.Country.CH.to_string()
"CH 93 00762 011623852957"
""" """
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@size 21 @size 21
@rule ~r/^(?<bank_code>[0-9]{5})(?<account_number>[0-9A-Z]{12})$/i @rule ~r/^(?<bank_code>[0-9]{5})(?<account_number>[0-9A-Z]{12})$/i
@spec size() :: 21 use IbanEx.Country.Template
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 end

View File

@@ -1,25 +1,24 @@
defmodule IbanEx.Country.CY do defmodule IbanEx.Country.CY do
@moduledoc """ @moduledoc """
Cyprus IBAN parsing rules Cyprus IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "CY", check_digits: "17", bank_code: "002", branch_code: "00128", national_check: nil, account_number: "0000001200527600"}
iex> |> IbanEx.Country.CY.to_string()
"CY 17 002 00128 0000001200527600"
""" """
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@size 28 @size 28
@rule ~r/^(?<bank_code>[0-9]{3})(?<branch_code>[0-9]{5})(?<account_number>[0-9A-Z]{16})$/i @rule ~r/^(?<bank_code>[0-9]{3})(?<branch_code>[0-9]{5})(?<account_number>[0-9A-Z]{16})$/i
@spec size() :: 28 use IbanEx.Country.Template
def size(), do: @size
@spec rule() :: Regex.t() @impl IbanEx.Country.Template
def rule(), do: @rule @spec to_string(Iban.t()) :: binary()
@spec to_string(Iban.t(), binary()) :: binary()
@spec to_s(Iban.t()) :: binary() def to_string(
@spec to_s(Iban.t(), binary()) :: binary()
def to_s(
%Iban{ %Iban{
country_code: country_code, country_code: country_code,
check_digits: check_digits, check_digits: check_digits,

View File

@@ -1,36 +1,17 @@
defmodule IbanEx.Country.CZ do defmodule IbanEx.Country.CZ do
@moduledoc """ @moduledoc """
Czech Republic IBAN parsing rules Czech Republic IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "CZ", check_digits: "65", bank_code: "0800", branch_code: nil, national_check: nil, account_number: "0000192000145399"}
iex> |> IbanEx.Country.CZ.to_string()
"CZ 65 0800 0000192000145399"
""" """
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@size 24 @size 24
@rule ~r/^(?<bank_code>[0-9]{4})(?<account_number>[0-9]{16})$/i @rule ~r/^(?<bank_code>[0-9]{4})(?<account_number>[0-9]{16})$/i
@spec size() :: 24 use IbanEx.Country.Template
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 end

View File

@@ -1,36 +1,17 @@
defmodule IbanEx.Country.DE do defmodule IbanEx.Country.DE do
@moduledoc """ @moduledoc """
Germany IBAN parsing rules Germany IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "DE", check_digits: "89", bank_code: "37040044", branch_code: nil, national_check: nil, account_number: "0532013000"}
iex> |> IbanEx.Country.DE.to_string()
"DE 89 37040044 0532013000"
""" """
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@size 22 @size 22
@rule ~r/^(?<bank_code>[0-9]{8})(?<account_number>[0-9]{10})$/i @rule ~r/^(?<bank_code>[0-9]{8})(?<account_number>[0-9]{10})$/i
@spec size() :: 22 use IbanEx.Country.Template
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 end

View File

@@ -1,36 +1,16 @@
defmodule IbanEx.Country.DK do defmodule IbanEx.Country.DK do
@moduledoc """ @moduledoc """
Denmark IBAN parsing rules Denmark IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "DK", check_digits: "50", bank_code: "0040", branch_code: nil, national_check: nil, account_number: "0440116243"}
iex> |> IbanEx.Country.DK.to_string()
"DK 50 0040 0440116243"
""" """
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@size 18 @size 18
@rule ~r/^(?<bank_code>[0-9]{4})(?<account_number>[0-9]{10})$/i @rule ~r/^(?<bank_code>[0-9]{4})(?<account_number>[0-9]{10})$/i
@spec size() :: 18 use IbanEx.Country.Template
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 end

View File

@@ -1,25 +1,24 @@
defmodule IbanEx.Country.EE do defmodule IbanEx.Country.EE do
@moduledoc """ @moduledoc """
Estonian IBAN parsing rules Estonian IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "EE", check_digits: "38", bank_code: "22", branch_code: "00", national_check: "5", account_number: "22102014568"}
iex> |> IbanEx.Country.EE.to_string()
"EE 38 22 00 22102014568 5"
""" """
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@size 20 @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 @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 use IbanEx.Country.Template
def size(), do: @size
@spec rule() :: Regex.t() @impl IbanEx.Country.Template
def rule(), do: @rule @spec to_string(Iban.t()) :: binary()
@spec to_string(Iban.t(), binary()) :: binary()
@spec to_s(Iban.t()) :: binary() def to_string(
@spec to_s(Iban.t(), binary()) :: binary()
def to_s(
%Iban{ %Iban{
country_code: country_code, country_code: country_code,
check_digits: check_digits, check_digits: check_digits,

View File

@@ -1,25 +1,24 @@
defmodule IbanEx.Country.ES do defmodule IbanEx.Country.ES do
@moduledoc """ @moduledoc """
Spain IBAN parsing rules Spain IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "ES", check_digits: "91", bank_code: "2100", branch_code: "0418", national_check: "45", account_number: "0200051332"}
iex> |> IbanEx.Country.ES.to_string()
"ES 91 2100 0418 45 0200051332"
""" """
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@size 24 @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 @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 use IbanEx.Country.Template
def size(), do: @size
@spec rule() :: Regex.t() @impl IbanEx.Country.Template
def rule(), do: @rule @spec to_string(Iban.t()) :: binary()
@spec to_string(Iban.t(), binary()) :: binary()
@spec to_s(Iban.t()) :: binary() def to_string(
@spec to_s(Iban.t(), binary()) :: binary()
def to_s(
%Iban{ %Iban{
country_code: country_code, country_code: country_code,
check_digits: check_digits, check_digits: check_digits,

View File

@@ -1,25 +1,24 @@
defmodule IbanEx.Country.FI do defmodule IbanEx.Country.FI do
@moduledoc """ @moduledoc """
Finland IBAN parsing rules Finland IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "FI", check_digits: "21", bank_code: "123456", branch_code: nil, national_check: "5", account_number: "0000078"}
iex> |> IbanEx.Country.FI.to_string()
"FI 21 123456 0000078 5"
""" """
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@size 18 @size 18
@rule ~r/^(?<bank_code>[0-9]{6})(?<account_number>[0-9]{7})(?<national_check>[0-9]{1})$/i @rule ~r/^(?<bank_code>[0-9]{6})(?<account_number>[0-9]{7})(?<national_check>[0-9]{1})$/i
@spec size() :: 18 use IbanEx.Country.Template
def size(), do: @size
@spec rule() :: Regex.t() @impl IbanEx.Country.Template
def rule(), do: @rule @spec to_string(Iban.t()) :: binary()
@spec to_string(Iban.t(), binary()) :: binary()
@spec to_s(Iban.t()) :: binary() def to_string(
@spec to_s(Iban.t(), binary()) :: binary()
def to_s(
%Iban{ %Iban{
country_code: country_code, country_code: country_code,
check_digits: check_digits, check_digits: check_digits,

View File

@@ -1,25 +1,24 @@
defmodule IbanEx.Country.FR do defmodule IbanEx.Country.FR do
@moduledoc """ @moduledoc """
France IBAN parsing rules France IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "FR", check_digits: "14", bank_code: "20041", branch_code: "01005", national_check: "06", account_number: "0500013M026"}
iex> |> IbanEx.Country.FR.to_string()
"FR 14 20041 01005 0500013M026 06"
""" """
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@size 27 @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 @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 use IbanEx.Country.Template
def size(), do: @size
@spec rule() :: Regex.t() @impl IbanEx.Country.Template
def rule(), do: @rule @spec to_string(Iban.t()) :: binary()
@spec to_string(Iban.t(), binary()) :: binary()
@spec to_s(Iban.t()) :: binary() def to_string(
@spec to_s(Iban.t(), binary()) :: binary()
def to_s(
%Iban{ %Iban{
country_code: country_code, country_code: country_code,
check_digits: check_digits, check_digits: check_digits,

View File

@@ -1,25 +1,24 @@
defmodule IbanEx.Country.GB do defmodule IbanEx.Country.GB do
@moduledoc """ @moduledoc """
United Kingdom IBAN parsing rules United Kingdom IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "GB", check_digits: "29", bank_code: "NWBK", branch_code: "601613", national_check: "06", account_number: "31926819"}
iex> |> IbanEx.Country.GB.to_string()
"GB 29 NWBK 601613 31926819"
""" """
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@size 22 @size 22
@rule ~r/^(?<bank_code>[A-Z]{4})(?<branch_code>[0-9]{6})(?<account_number>[0-9]{8})$/i @rule ~r/^(?<bank_code>[A-Z]{4})(?<branch_code>[0-9]{6})(?<account_number>[0-9]{8})$/i
@spec size() :: 22 use IbanEx.Country.Template
def size(), do: @size
@spec rule() :: Regex.t() @impl IbanEx.Country.Template
def rule(), do: @rule @spec to_string(Iban.t()) :: binary()
@spec to_string(Iban.t(), binary()) :: binary()
@spec to_s(Iban.t()) :: binary() def to_string(
@spec to_s(Iban.t(), binary()) :: binary()
def to_s(
%Iban{ %Iban{
country_code: country_code, country_code: country_code,
check_digits: check_digits, check_digits: check_digits,

17
lib/iban_ex/country/gi.ex Normal file
View File

@@ -0,0 +1,17 @@
defmodule IbanEx.Country.GI do
@moduledoc """
Gibraltar IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "GI", check_digits: "75", bank_code: "NWBK", branch_code: nil, national_check: nil, account_number: "000000007099453"}
iex> |> IbanEx.Country.GI.to_string()
"GI 75 NWBK 000000007099453"
"""
@size 23
@rule ~r/^(?<bank_code>[A-Z]{4})(?<account_number>[0-9A-Z]{15})$/i
use IbanEx.Country.Template
end

35
lib/iban_ex/country/gr.ex Normal file
View File

@@ -0,0 +1,35 @@
defmodule IbanEx.Country.GR do
@moduledoc """
Greece IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "GR", check_digits: "16", bank_code: "011", branch_code: "0125", national_check: nil, account_number: "0000000012300695"}
iex> |> IbanEx.Country.GR.to_string()
"GR 16 011 0125 0000000012300695"
"""
@size 27
@rule ~r/^(?<bank_code>[0-9]{3})(?<branch_code>[0-9]{4})(?<account_number>[0-9A-Z]{16})$/i
use IbanEx.Country.Template
@impl IbanEx.Country.Template
@spec to_string(Iban.t()) :: binary()
@spec to_string(Iban.t(), binary()) :: binary()
def to_string(
%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

View File

@@ -1,36 +1,17 @@
defmodule IbanEx.Country.HR do defmodule IbanEx.Country.HR do
@moduledoc """ @moduledoc """
Croatia IBAN parsing rules Croatia IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "HR", check_digits: "12", bank_code: "1001005", branch_code: nil, national_check: nil, account_number: "1863000160"}
iex> |> IbanEx.Country.HR.to_string()
"HR 12 1001005 1863000160"
""" """
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@size 21 @size 21
@rule ~r/^(?<bank_code>[0-9]{7})(?<account_number>[0-9]{10})$/i @rule ~r/^(?<bank_code>[0-9]{7})(?<account_number>[0-9]{10})$/i
@spec size() :: 21 use IbanEx.Country.Template
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 end

35
lib/iban_ex/country/hu.ex Normal file
View File

@@ -0,0 +1,35 @@
defmodule IbanEx.Country.HU do
@moduledoc """
Hungary IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "HU", check_digits: "42", bank_code: "117", branch_code: "7301", national_check: "0", account_number: "6111110180000000"}
iex> |> IbanEx.Country.HU.to_string()
"HU 42 117 7301 6111110180000000 0"
"""
@size 28
@rule ~r/^(?<bank_code>[0-9]{3})(?<branch_code>[0-9]{4})(?<account_number>[0-9]{16})(?<national_check>[0-9]{1})$/i
use IbanEx.Country.Template
@impl IbanEx.Country.Template
@spec to_string(Iban.t()) :: binary()
@spec to_string(Iban.t(), binary()) :: binary()
def to_string(
%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

35
lib/iban_ex/country/ie.ex Normal file
View File

@@ -0,0 +1,35 @@
defmodule IbanEx.Country.IE do
@moduledoc """
Ireland IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "IE", check_digits: "29", bank_code: "AIBK", branch_code: "931152", national_check: nil, account_number: "12345678"}
iex> |> IbanEx.Country.IE.to_string()
"IE 29 AIBK 931152 12345678"
"""
@size 22
@rule ~r/^(?<bank_code>[A-Z]{4})(?<branch_code>[0-9]{6})(?<account_number>[0-9]{8})$/i
use IbanEx.Country.Template
@impl IbanEx.Country.Template
@spec to_string(Iban.t()) :: binary()
@spec to_string(Iban.t(), binary()) :: binary()
def to_string(
%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

35
lib/iban_ex/country/it.ex Normal file
View File

@@ -0,0 +1,35 @@
defmodule IbanEx.Country.IT do
@moduledoc """
Italy IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "IT", check_digits: "60", bank_code: "05428", branch_code: "11101", national_check: "X", account_number: "000000123456"}
iex> |> IbanEx.Country.IT.to_string()
"IT 60 X 05428 11101 000000123456"
"""
@size 27
@rule ~r/^(?<national_check>[A-Z]{1})(?<bank_code>[0-9]{5})(?<branch_code>[0-9]{5})(?<account_number>[0-9A-Z]{12})$/i
use IbanEx.Country.Template
@impl IbanEx.Country.Template
@spec to_string(Iban.t()) :: binary()
@spec to_string(Iban.t(), binary()) :: binary()
def to_string(
%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, national_check, bank_code, branch_code, account_number]
|> Enum.join(joiner)
end
end

17
lib/iban_ex/country/li.ex Normal file
View File

@@ -0,0 +1,17 @@
defmodule IbanEx.Country.LI do
@moduledoc """
Liechtenstein IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "LI", check_digits: "21", bank_code: "08810", branch_code: nil, national_check: nil, account_number: "0002324013AA"}
iex> |> IbanEx.Country.LI.to_string()
"LI 21 08810 0002324013AA"
"""
@size 21
@rule ~r/^(?<bank_code>[0-9]{5})(?<account_number>[0-9A-Z]{12})$/i
use IbanEx.Country.Template
end

View File

@@ -1,36 +1,17 @@
defmodule IbanEx.Country.LT do defmodule IbanEx.Country.LT do
@moduledoc """ @moduledoc """
Lithuanian IBAN parsing rules Lithuanian IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "LT", check_digits: "12", bank_code: "10000", branch_code: nil, national_check: nil, account_number: "11101001000"}
iex> |> IbanEx.Country.LT.to_string()
"LT 12 10000 11101001000"
""" """
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@size 20 @size 20
@rule ~r/^(?<bank_code>[0-9]{5})(?<account_number>[0-9]{11})$/i @rule ~r/^(?<bank_code>[0-9]{5})(?<account_number>[0-9]{11})$/i
@spec size() :: 20 use IbanEx.Country.Template
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 end

View File

@@ -1,36 +1,17 @@
defmodule IbanEx.Country.LU do defmodule IbanEx.Country.LU do
@moduledoc """ @moduledoc """
Luxembourg IBAN parsing rules Luxembourg IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "LU", check_digits: "28", bank_code: "001", branch_code: nil, national_check: nil, account_number: "9400644750000"}
iex> |> IbanEx.Country.LU.to_string()
"LU 28 001 9400644750000"
""" """
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@size 20 @size 20
@rule ~r/^(?<bank_code>[0-9]{3})(?<account_number>[0-9A-Z]{13})$/i @rule ~r/^(?<bank_code>[0-9]{3})(?<account_number>[0-9A-Z]{13})$/i
@spec size() :: 20 use IbanEx.Country.Template
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 end

View File

@@ -1,36 +1,17 @@
defmodule IbanEx.Country.LV do defmodule IbanEx.Country.LV do
@moduledoc """ @moduledoc """
Latvian IBAN parsing rules Latvian IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "LV", check_digits: "80", bank_code: "BANK", branch_code: nil, national_check: nil, account_number: "0000435195001"}
iex> |> IbanEx.Country.LV.to_string()
"LV 80 BANK 0000435195001"
""" """
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@size 21 @size 21
@rule ~r/^(?<bank_code>[A-Z]{4})(?<account_number>[0-9A-Z]{13})$/i @rule ~r/^(?<bank_code>[A-Z]{4})(?<account_number>[0-9A-Z]{13})$/i
@spec size() :: 21 use IbanEx.Country.Template
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 end

35
lib/iban_ex/country/mc.ex Normal file
View File

@@ -0,0 +1,35 @@
defmodule IbanEx.Country.MC do
@moduledoc """
Monaco IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "MC", check_digits: "58", bank_code: "11222", branch_code: "00001", national_check: "30", account_number: "01234567890"}
iex> |> IbanEx.Country.MC.to_string()
"MC 58 11222 00001 01234567890 30"
"""
@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
use IbanEx.Country.Template
@impl IbanEx.Country.Template
@spec to_string(Iban.t()) :: binary()
@spec to_string(Iban.t(), binary()) :: binary()
def to_string(
%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

View File

@@ -1,25 +1,24 @@
defmodule IbanEx.Country.MT do defmodule IbanEx.Country.MT do
@moduledoc """ @moduledoc """
Malta IBAN parsing rules Malta IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "MT", check_digits: "84", bank_code: "MALT", branch_code: "01100", national_check: nil, account_number: "0012345MTLCAST001S"}
iex> |> IbanEx.Country.MT.to_string()
"MT 84 MALT 01100 0012345MTLCAST001S"
""" """
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@size 31 @size 31
@rule ~r/^(?<bank_code>[A-Z]{4})(?<branch_code>[0-9]{5})(?<account_number>[0-9A-Z]{18})$/i @rule ~r/^(?<bank_code>[A-Z]{4})(?<branch_code>[0-9]{5})(?<account_number>[0-9A-Z]{18})$/i
@spec size() :: 31 use IbanEx.Country.Template
def size(), do: @size
@spec rule() :: Regex.t() @impl IbanEx.Country.Template
def rule(), do: @rule @spec to_string(Iban.t()) :: binary()
@spec to_string(Iban.t(), binary()) :: binary()
@spec to_s(Iban.t()) :: binary() def to_string(
@spec to_s(Iban.t(), binary()) :: binary()
def to_s(
%Iban{ %Iban{
country_code: country_code, country_code: country_code,
check_digits: check_digits, check_digits: check_digits,

View File

@@ -1,36 +1,17 @@
defmodule IbanEx.Country.NL do defmodule IbanEx.Country.NL do
@moduledoc """ @moduledoc """
Netherlands IBAN parsing rules Netherlands IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "NL", check_digits: "91", bank_code: "ABNA", branch_code: nil, national_check: nil, account_number: "0417164300"}
iex> |> IbanEx.Country.NL.to_string()
"NL 91 ABNA 0417164300"
""" """
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@size 18 @size 18
@rule ~r/^(?<bank_code>[A-Z]{4})(?<account_number>[0-9]{10})$/i @rule ~r/^(?<bank_code>[A-Z]{4})(?<account_number>[0-9]{10})$/i
@spec size() :: 18 use IbanEx.Country.Template
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 end

35
lib/iban_ex/country/no.ex Normal file
View File

@@ -0,0 +1,35 @@
defmodule IbanEx.Country.NO do
@moduledoc """
Norway IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "NO", check_digits: "93", bank_code: "8601", branch_code: nil, national_check: "7", account_number: "111794"}
iex> |> IbanEx.Country.NO.to_string()
"NO 93 8601 111794 7"
"""
@size 15
@rule ~r/^(?<bank_code>[0-9]{4})(?<account_number>[0-9]{6})(?<national_check>[0-9]{1})$/i
use IbanEx.Country.Template
@impl IbanEx.Country.Template
@spec to_string(Iban.t()) :: binary()
@spec to_string(Iban.t(), binary()) :: binary()
def to_string(
%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

View File

@@ -1,25 +1,24 @@
defmodule IbanEx.Country.PL do defmodule IbanEx.Country.PL do
@moduledoc """ @moduledoc """
Poland IBAN parsing rules Poland IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "PL", check_digits: "61", bank_code: "109", branch_code: "0101", national_check: "4", account_number: "0000071219812874"}
iex> |> IbanEx.Country.PL.to_string()
"PL 61 109 0101 4 0000071219812874"
""" """
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@size 28 @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 @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 use IbanEx.Country.Template
def size(), do: @size
@spec rule() :: Regex.t() @impl IbanEx.Country.Template
def rule(), do: @rule @spec to_string(Iban.t()) :: binary()
@spec to_string(Iban.t(), binary()) :: binary()
@spec to_s(Iban.t()) :: binary() def to_string(
@spec to_s(Iban.t(), binary()) :: binary()
def to_s(
%Iban{ %Iban{
country_code: country_code, country_code: country_code,
check_digits: check_digits, check_digits: check_digits,

View File

@@ -1,25 +1,24 @@
defmodule IbanEx.Country.PT do defmodule IbanEx.Country.PT do
@moduledoc """ @moduledoc """
Portugal IBAN parsing rules Portugal IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "PT", check_digits: "50", bank_code: "0002", branch_code: "0123", national_check: "54", account_number: "12345678901"}
iex> |> IbanEx.Country.PT.to_string()
"PT 50 0002 0123 12345678901 54"
""" """
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@size 25 @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 @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 use IbanEx.Country.Template
def size(), do: @size
@spec rule() :: Regex.t() @impl IbanEx.Country.Template
def rule(), do: @rule @spec to_string(Iban.t()) :: binary()
@spec to_string(Iban.t(), binary()) :: binary()
@spec to_s(Iban.t()) :: binary() def to_string(
@spec to_s(Iban.t(), binary()) :: binary()
def to_s(
%Iban{ %Iban{
country_code: country_code, country_code: country_code,
check_digits: check_digits, check_digits: check_digits,

17
lib/iban_ex/country/ro.ex Normal file
View File

@@ -0,0 +1,17 @@
defmodule IbanEx.Country.RO do
@moduledoc """
Romania IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "RO", check_digits: "49", bank_code: "AAAA", branch_code: nil, national_check: nil, account_number: "1B31007593840000"}
iex> |> IbanEx.Country.RO.to_string()
"RO 49 AAAA 1B31007593840000"
"""
@size 24
@rule ~r/^(?<bank_code>[A-Z]{4})(?<account_number>[0-9A-Z]{16})$/i
use IbanEx.Country.Template
end

17
lib/iban_ex/country/se.ex Normal file
View File

@@ -0,0 +1,17 @@
defmodule IbanEx.Country.SE do
@moduledoc """
Sweden IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "SE", check_digits: "45", bank_code: "500", branch_code: nil, national_check: nil, account_number: "00000058398257466"}
iex> |> IbanEx.Country.SE.to_string()
"SE 45 500 00000058398257466"
"""
@size 24
@rule ~r/^(?<bank_code>[0-9]{3})(?<account_number>[0-9]{17})$/i
use IbanEx.Country.Template
end

37
lib/iban_ex/country/si.ex Normal file
View File

@@ -0,0 +1,37 @@
defmodule IbanEx.Country.SI do
@moduledoc """
Slovenia IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "SI", check_digits: "56", bank_code: "26", branch_code: "330", national_check: "86", account_number: "00120390"}
iex> |> IbanEx.Country.SI.to_string()
"SI 56 26 330 00120390 86"
"""
@size 19
@rule ~r/^(?<bank_code>[0-9]{2})(?<branch_code>[0-9]{3})(?<account_number>[0-9]{8})(?<national_check>[0-9]{2})$/i
use IbanEx.Country.Template
@impl IbanEx.Country.Template
@spec to_string(Iban.t()) :: binary()
@spec to_string(Iban.t(), binary()) :: binary()
def to_string(
%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

17
lib/iban_ex/country/sk.ex Normal file
View File

@@ -0,0 +1,17 @@
defmodule IbanEx.Country.SK do
@moduledoc """
Slovakia IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "SK", check_digits: "31", bank_code: "1200", branch_code: nil, national_check: nil, account_number: "0000198742637541"}
iex> |> IbanEx.Country.SK.to_string()
"SK 31 1200 0000198742637541"
"""
@size 24
@rule ~r/^(?<bank_code>[0-9]{4})(?<account_number>[0-9]{16})$/i
use IbanEx.Country.Template
end

35
lib/iban_ex/country/sm.ex Normal file
View File

@@ -0,0 +1,35 @@
defmodule IbanEx.Country.SM do
@moduledoc """
San Marino IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "SM", check_digits: "86", bank_code: "03225", branch_code: "09800", national_check: "U", account_number: "000000270100"}
iex> |> IbanEx.Country.SM.to_string()
"SM 86 U 03225 09800 000000270100"
"""
@size 27
@rule ~r/^(?<national_check>[A-Z]{1})(?<bank_code>[0-9]{5})(?<branch_code>[0-9]{5})(?<account_number>[0-9A-Z]{12})$/i
use IbanEx.Country.Template
@impl IbanEx.Country.Template
@spec to_string(Iban.t()) :: binary()
@spec to_string(Iban.t(), binary()) :: binary()
def to_string(
%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, national_check, bank_code, branch_code, account_number]
|> Enum.join(joiner)
end
end

View File

@@ -1,4 +1,6 @@
defmodule IbanEx.Country.Template do defmodule IbanEx.Country.Template do
@moduledoc false
alias IbanEx.Iban alias IbanEx.Iban
@type size() :: non_neg_integer() @type size() :: non_neg_integer()
@type rule() :: Regex.t() @type rule() :: Regex.t()
@@ -7,6 +9,41 @@ defmodule IbanEx.Country.Template do
@callback size() :: size() @callback size() :: size()
@callback rule() :: rule() @callback rule() :: rule()
@callback to_s(Iban.t(), joiner()) :: String.t() @callback to_string(Iban.t(), joiner()) :: String.t()
@callback to_s(Iban.t()) :: String.t() @callback to_string(Iban.t()) :: String.t()
defmacro __using__(_opts) do
quote do
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@impl IbanEx.Country.Template
@spec to_string(Iban.t()) :: binary()
@spec to_string(Iban.t(), binary()) :: binary()
def to_string(
%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
@impl IbanEx.Country.Template
@spec size() :: Integer.t()
def size(), do: @size
@impl IbanEx.Country.Template
@spec rule() :: Regex.t()
def rule(), do: @rule
defoverridable to_string: 1, to_string: 2, size: 0, rule: 0
end
end
end end

View File

@@ -1,36 +1,16 @@
defmodule IbanEx.Country.UA do defmodule IbanEx.Country.UA do
@moduledoc """ @moduledoc """
Ukrainian IBAN parsing rules Ukrainian IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "UA", check_digits: "21", bank_code: "322313", branch_code: nil, national_check: nil, account_number: "0000026007233566001"}
iex> |> IbanEx.Country.UA.to_string()
"UA 21 322313 0000026007233566001"
""" """
alias IbanEx.Iban
@behaviour IbanEx.Country.Template
@size 29 @size 29
@rule ~r/^(?<bank_code>[0-9]{6})(?<account_number>[0-9A-Z]{19})$/i @rule ~r/^(?<bank_code>[0-9]{6})(?<account_number>[0-9A-Z]{19})$/i
use IbanEx.Country.Template
@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 end

17
lib/iban_ex/country/va.ex Normal file
View File

@@ -0,0 +1,17 @@
defmodule IbanEx.Country.VA do
@moduledoc """
Vatican IBAN parsing rules
## Examples
iex> %IbanEx.Iban{country_code: "VA", check_digits: "59", bank_code: "001", branch_code: nil, national_check: nil, account_number: "123000012345678"}
iex> |> IbanEx.Country.VA.to_string()
"VA 59 001 123000012345678"
"""
@size 22
@rule ~r/^(?<bank_code>[0-9]{3})(?<account_number>[0-9]{15})$/i
use IbanEx.Country.Template
end

View File

@@ -7,8 +7,8 @@ def to_iban(value)
defimpl IbanEx.Deserialize, for: [BitString, String] do defimpl IbanEx.Deserialize, for: [BitString, String] do
alias IbanEx.{Parser, Error} alias IbanEx.{Parser, Error}
@type iban_or_error() :: IbanEx.Iban.t() | {:error, atom()} @type iban_or_error() :: IbanEx.Iban.t() | {:error, atom()}
@spec to_iban(binary()) :: iban_or_error()
@spec to_iban(String.t()) :: iban_or_error() @spec to_iban(String.t()) :: iban_or_error()
@spec to_iban(binary()) :: IbanEx.Iban.t()
def to_iban(string) do def to_iban(string) do
case Parser.parse(string) do case Parser.parse(string) do
{:ok, iban} -> iban {:ok, iban} -> iban

View File

@@ -1,7 +1,5 @@
defmodule IbanEx.Error do defmodule IbanEx.Error do
@moduledoc """ @moduledoc false
"""
@type error() :: @type error() ::
:unsupported_country_code :unsupported_country_code
@@ -9,6 +7,8 @@ defmodule IbanEx.Error do
| :invalid_length | :invalid_length
| :invalid_checksum | :invalid_checksum
| :can_not_parse_map | :can_not_parse_map
| :length_to_long
| :length_to_short
| atom() | atom()
@type errors() :: [error()] @type errors() :: [error()]
@errors [ @errors [
@@ -16,15 +16,19 @@ defmodule IbanEx.Error do
:invalid_format, :invalid_format,
:invalid_length, :invalid_length,
:invalid_checksum, :invalid_checksum,
:can_not_parse_map :can_not_parse_map,
] :length_to_long,
:length_to_short
]
@messages [ @messages [
unsupported_country_code: "Unsupported country code", unsupported_country_code: "Unsupported country code",
invalid_format: "IBAN violates required format", invalid_format: "IBAN violates required format",
invalid_length: "IBAN violates the required length", invalid_length: "IBAN violates the required length",
invalid_checksum: "IBAN's checksum is invalid", invalid_checksum: "IBAN's checksum is invalid",
can_not_parse_map: "Can't parse map to IBAN struct" can_not_parse_map: "Can't parse map to IBAN struct",
length_to_long: "IBAN longer then required length",
length_to_short: "IBAN shorter then required length"
] ]
@spec message(error()) :: String.t() @spec message(error()) :: String.t()

View File

@@ -1,4 +1,6 @@
defmodule IbanEx.Formatter do defmodule IbanEx.Formatter do
@moduledoc false
alias IbanEx.Country alias IbanEx.Country
import IbanEx.Commons, only: [normalize: 1] import IbanEx.Commons, only: [normalize: 1]
@@ -28,7 +30,7 @@ def format(iban, :compact),
def format(iban, :pretty) do def format(iban, :pretty) do
country_module = Country.country_module(iban.country_code) country_module = Country.country_module(iban.country_code)
country_module.to_s(iban) country_module.to_string(iban)
end end
def format(iban, :splitted) do def format(iban, :splitted) do

View File

@@ -1,4 +1,6 @@
defmodule IbanEx.Iban do defmodule IbanEx.Iban do
@moduledoc false
alias IbanEx.Formatter alias IbanEx.Formatter
alias IbanEx.{Serialize} alias IbanEx.{Serialize}

View File

@@ -1,4 +1,6 @@
defmodule IbanEx.Parser do defmodule IbanEx.Parser do
@moduledoc false
alias IbanEx.{Country, Iban, Validator} alias IbanEx.{Country, Iban, Validator}
import IbanEx.Commons, only: [normalize_and_slice: 2] import IbanEx.Commons, only: [normalize_and_slice: 2]
@@ -9,23 +11,35 @@ defmodule IbanEx.Parser do
@spec parse({:ok, String.t()} | String.t()) :: iban_or_error() @spec parse({:ok, String.t()} | String.t()) :: iban_or_error()
def parse({:ok, iban_string}), do: parse(iban_string) def parse({:ok, iban_string}), do: parse(iban_string)
def parse(iban_string) do def parse(iban_string) do
with {:ok, valid_iban} <- Validator.validate(iban_string) do case Validator.validate(iban_string) do
iban_map = %{ {:ok, valid_iban} ->
country_code: country_code(valid_iban), iban_map = %{
check_digits: check_digits(valid_iban), country_code: country_code(valid_iban),
} check_digits: check_digits(valid_iban)
}
regex = Country.country_module(iban_map.country_code).rule() bban_map =
bban = bban(iban_string) iban_string
bban_map = for {key, val} <- Regex.named_captures(regex, bban), into: %{}, do: {String.to_atom(key), val} |> bban()
|> parse_bban(iban_map.country_code)
{:ok, struct(Iban, Map.merge(iban_map, bban_map))} {:ok, struct(Iban, Map.merge(iban_map, bban_map))}
else
{:error, error_type} -> {:error, error_type} {:error, error_type} ->
{:error, error_type}
end end
end end
@spec parse_bban(binary(), <<_::16>>) :: map()
def parse_bban(bban_string, country_code) do
regex = Country.country_module(country_code).rule()
for {key, val} <- Regex.named_captures(regex, bban_string),
into: %{},
do: {String.to_atom(key), val}
end
@spec country_code(iban_string()) :: country_code_string() @spec country_code(iban_string()) :: country_code_string()
def country_code(iban_string), do: normalize_and_slice(iban_string, 0..1) def country_code(iban_string), do: normalize_and_slice(iban_string, 0..1)

View File

@@ -1,4 +1,6 @@
defmodule IbanEx.Serialize do defmodule IbanEx.Serialize do
@moduledoc false
alias IbanEx.{Iban, Formatter} alias IbanEx.{Iban, Formatter}
@spec to_string(Iban.t()) :: String.t() @spec to_string(Iban.t()) :: String.t()

View File

@@ -1,28 +1,53 @@
defmodule IbanEx.Validator do defmodule IbanEx.Validator do
@moduledoc false
alias IbanEx.{Country, Parser} alias IbanEx.{Country, Parser}
alias IbanEx.Validator.Replacements alias IbanEx.Validator.Replacements
import IbanEx.Commons, only: [normalize: 1] import IbanEx.Commons, only: [normalize: 1]
@spec validate(String.t()) :: {:ok, String.t()} | {:error} defp error_accumulator(acc, error_message)
defp error_accumulator(acc, {:error, error}), do: [error | acc]
defp error_accumulator(acc, _), do: acc
defp violation_functions(),
do: [
{&__MODULE__.iban_violates_format?/1, {:error, :invalid_format}},
{&__MODULE__.iban_unsupported_country?/1, {:error, :unsupported_country_code}},
{&__MODULE__.iban_violates_length?/1, {:error, :invalid_length}},
{&__MODULE__.iban_violates_country_rule?/1, {:error, :invalid_format}},
{&__MODULE__.iban_violates_checksum?/1, {:error, :invalid_checksum}}
]
@doc """
Accumulate check results in the list of errors
Check iban_violates_format?, iban_unsupported_country?, iban_violates_length?, iban_violates_country_rule?, iban_violates_checksum?
"""
@spec violations(String.t()) :: [] | [atom()]
def violations(iban) do
violation_functions()
|> Enum.reduce([], fn {fun, value}, acc -> error_accumulator(acc, !fun.(iban) or value) end)
|> Enum.reverse()
end
@doc """
Make checks in this order step-by-step before first error ->
iban_violates_format?,
iban_unsupported_country?,
iban_violates_length?,
iban_violates_country_rule?,
iban_violates_checksum?
"""
@spec validate(String.t()) :: {:ok, String.t()} | {:error, Atom.t()}
def validate(iban) do def validate(iban) do
cond do cond do
iban_violates_format?(iban) -> iban_violates_format?(iban) -> {:error, :invalid_format}
{:error, :invalid_format} iban_unsupported_country?(iban) -> {:error, :unsupported_country_code}
iban_violates_length?(iban) -> {:error, :invalid_length}
iban_unsupported_country?(iban) -> iban_violates_country_rule?(iban) -> {:error, :invalid_format}
{:error, :unsupported_country_code} iban_violates_checksum?(iban) -> {:error, :invalid_checksum}
true -> {:ok, normalize(iban)}
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
end end
@@ -35,12 +60,12 @@ defp size(iban) do
# - Check whether a given IBAN violates the required format. # - Check whether a given IBAN violates the required format.
@spec iban_violates_format?(String.t()) :: boolean @spec iban_violates_format?(String.t()) :: boolean
defp iban_violates_format?(iban), def iban_violates_format?(iban),
do: Regex.match?(~r/[^A-Z0-9]/i, normalize(iban)) do: Regex.match?(~r/[^A-Z0-9]/i, normalize(iban))
# - Check whether a given IBAN violates the supported countries. # - Check whether a given IBAN violates the supported countries.
@spec iban_unsupported_country?(String.t()) :: boolean @spec iban_unsupported_country?(String.t()) :: boolean
defp iban_unsupported_country?(iban) do def iban_unsupported_country?(iban) do
supported? = supported? =
iban iban
|> Parser.country_code() |> Parser.country_code()
@@ -49,33 +74,52 @@ defp iban_unsupported_country?(iban) do
!supported? !supported?
end end
# - Check whether a given IBAN violates the required length. @doc "Check whether a given IBAN violates the required length."
@spec iban_violates_length?(String.t()) :: boolean @spec iban_violates_length?(String.t()) :: boolean
defp iban_violates_length?(iban) do def iban_violates_length?(iban) do
with country_code <- Parser.country_code(iban), with country_code <- Parser.country_code(iban),
country_module <- Country.country_module(country_code) do country_module when is_atom(country_module) <- Country.country_module(country_code) do
size(iban) != country_module.size() size(iban) != country_module.size()
else else
{:error, _} -> true {:error, _error} -> true
end end
end end
# - Check whether a given IBAN violates the country rules. @doc "Check length of IBAN"
@spec check_iban_length(String.t()) :: {:error, :length_to_short | :length_to_long } | :ok
def check_iban_length(iban) do
unless iban_unsupported_country?(iban) do
country_module =
iban
|> Parser.country_code()
|> Country.country_module()
case country_module.size() - size(iban) do
diff when diff > 0 -> {:error, :length_to_short}
diff when diff < 0 -> {:error, :length_to_long}
0 -> :ok
end
else
{:error, :unsupported_country_code}
end
end
@doc "Check whether a given IBAN violates the country rules"
@spec iban_violates_country_rule?(String.t()) :: boolean @spec iban_violates_country_rule?(String.t()) :: boolean
defp iban_violates_country_rule?(iban) do def iban_violates_country_rule?(iban) do
with country_code <- Parser.country_code(iban), with country_code <- Parser.country_code(iban),
bban <- Parser.bban(iban), bban <- Parser.bban(iban),
country_module <- Country.country_module(country_code), country_module when is_atom(country_module) <- Country.country_module(country_code),
rule <- country_module.rule() do rule <- country_module.rule() do
!Regex.match?(rule, bban) !Regex.match?(rule, bban)
else else
{:error, _} -> true {:error, _error} -> true
end end
end end
# - Check whether a given IBAN violates the required checksum. @doc "Check whether a given IBAN violates the required checksum."
@spec iban_violates_checksum?(String.t()) :: boolean @spec iban_violates_checksum?(String.t()) :: boolean
defp iban_violates_checksum?(iban) do def iban_violates_checksum?(iban) do
check_sum_base = Parser.bban(iban) <> Parser.country_code(iban) <> "00" check_sum_base = Parser.bban(iban) <> Parser.country_code(iban) <> "00"
replacements = Replacements.replacements() replacements = Replacements.replacements()

View File

@@ -2,7 +2,7 @@ defmodule IbanEx.MixProject do
use Mix.Project use Mix.Project
@source_url "https://g.tulz.dev/opensource/iban-ex" @source_url "https://g.tulz.dev/opensource/iban-ex"
@version "0.1.0" @version "0.1.5"
def project do def project do
[ [
@@ -68,7 +68,6 @@ defp deps do
{:ex_doc, ">= 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}, {:sobelow, ">= 0.0.0", only: ~w(dev test)a, runtime: false},
{:mix_audit, ">= 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}, {:observer_cli, "~> 1.7.4", only: :dev, runtime: false},
{:elixir_sense, github: "elixir-lsp/elixir_sense", only: ~w(dev)a} {:elixir_sense, github: "elixir-lsp/elixir_sense", only: ~w(dev)a}

View File

@@ -1,8 +1,130 @@
defmodule IbanExTest do defmodule IbanExTest do
use ExUnit.Case alias IbanEx.{Country, Iban, Parser}
doctest IbanEx use ExUnit.Case, async: true
doctest_file "README.md"
doctest IbanEx.Country.AT
doctest IbanEx.Country.BE
doctest IbanEx.Country.BG
doctest IbanEx.Country.CH
doctest IbanEx.Country.CY
doctest IbanEx.Country.CZ
doctest IbanEx.Country.DE
doctest IbanEx.Country.DK
doctest IbanEx.Country.EE
doctest IbanEx.Country.ES
doctest IbanEx.Country.FI
doctest IbanEx.Country.FR
doctest IbanEx.Country.GB
doctest IbanEx.Country.GI
doctest IbanEx.Country.GR
doctest IbanEx.Country.HR
doctest IbanEx.Country.HU
doctest IbanEx.Country.IE
doctest IbanEx.Country.IT
doctest IbanEx.Country.LI
doctest IbanEx.Country.LT
doctest IbanEx.Country.LU
doctest IbanEx.Country.LV
doctest IbanEx.Country.MC
doctest IbanEx.Country.MT
doctest IbanEx.Country.NL
doctest IbanEx.Country.NO
doctest IbanEx.Country.PL
doctest IbanEx.Country.PT
doctest IbanEx.Country.RO
doctest IbanEx.Country.SE
doctest IbanEx.Country.SM
doctest IbanEx.Country.SI
doctest IbanEx.Country.SK
doctest IbanEx.Country.UA
doctest IbanEx.Country.VA
test "greets the world" do @ibans [
assert IbanEx.hello() == :world "AL47212110090000000235698741",
"AD1200012030200359100100",
"AT611904300234573201",
"AZ21NABZ00000000137010001944",
"BH67BMAG00001299123456",
"BE68539007547034",
"BA391290079401028494",
"BR1800360305000010009795493C1",
"BG80BNBG96611020345678",
"CR05015202001026284066",
"HR1210010051863000160",
"CY17002001280000001200527600",
"CZ6508000000192000145399",
"DK5000400440116243",
"DO28BAGR00000001212453611324",
"EG380019000500000000263180002",
"SV62CENR00000000000000700025",
"EE382200221020145685",
"FO6264600001631634",
"FI2112345600000785",
"FR1420041010050500013M02606",
"GE29NB0000000101904917",
"DE89370400440532013000",
"GI75NWBK000000007099453",
"GR1601101250000000012300695",
"GL8964710001000206",
"GT82TRAJ01020000001210029690",
"HU42117730161111101800000000",
"IS140159260076545510730339",
"IE29AIBK93115212345678",
"IL620108000000099999999",
"IT60X0542811101000000123456",
"JO94CBJO0010000000000131000302",
"KZ86125KZT5004100100",
"XK051212012345678906",
"KW81CBKU0000000000001234560101",
"LV80BANK0000435195001",
"LB62099900000001001901229114",
"LI21088100002324013AA",
"LT121000011101001000",
"LU280019400644750000",
"MK07250120000058984",
"MT84MALT011000012345MTLCAST001S",
"MR1300020001010000123456753",
"MC5811222000010123456789030",
"ME25505000012345678951",
"NL91ABNA0417164300",
"NO9386011117947",
"PK36SCBL0000001123456702",
"PL61109010140000071219812874",
"PT50000201231234567890154",
"QA58DOHB00001234567890ABCDEFG",
"MD24AG000225100013104168",
"RO49AAAA1B31007593840000",
"SM86U0322509800000000270100",
"SA0380000000608010167519",
"RS35260005601001611379",
"SK3112000000198742637541",
"SI56263300012039086",
"ES9121000418450200051332",
"SE4550000000058398257466",
"CH9300762011623852957",
"TL380080012345678910157",
"TR330006100519786457841326",
"UA213223130000026007233566001",
"AE070331234567890123456",
"GB29NWBK60161331926819",
"VA59001123000012345678",
"VG96VPVG0000012345678901"
]
test "parsing valid IBANs from available countries returns {:ok, %IbanEx.Iban{}}" do
assert Enum.all?(@ibans, fn iban ->
iban_country = iban |> String.upcase() |> String.slice(0..1)
case {Country.is_country_code_supported?(iban_country), Parser.parse(iban)} do
{true, {:ok, %Iban{}}} ->
true
{false, {:error, :unsupported_country_code}} ->
true
_ ->
false
end
end)
end end
end end

View File

@@ -0,0 +1,18 @@
defmodule IbanExValidatorTest do
alias IbanEx.{Validator}
use ExUnit.Case, async: true
test "check IBANs length" do
cases = [
{"FG2112345CC6000007", {:error, :unsupported_country_code}},
{"UK2112345CC6000007", {:error, :unsupported_country_code}},
{"FI2112345CC6000007", :ok},
{"FI2112345CC6000007a", {:error, :length_to_long}},
{"FI2112345CC600007", {:error, :length_to_short}}
]
Enum.all?(cases, fn {iban, result} ->
assert Validator.check_iban_length(iban) == result
end)
end
end