Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions lib/bankster/bic.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ defmodule Bankster.Bic do
branch: binary() | nil
}

@bic_regex ~r/^(?<bank>[a-zA-Z]{4})\s*(?<country>[a-zA-Z]{2})\s*(?<location>[0-9a-zA-Z]{2})\s*(?<branch>[0-9a-zA-Z]{3})?$/

@doc """
Parses a BIC string into its components.

Expand All @@ -40,7 +38,7 @@ defmodule Bankster.Bic do
"""
@spec parse(term()) :: {:ok, t()} | :error
def parse(bic) when is_binary(bic) do
case Regex.named_captures(@bic_regex, bic) do
case Regex.named_captures(bic_regex(), bic) do
nil ->
:error

Expand Down Expand Up @@ -152,11 +150,15 @@ defmodule Bankster.Bic do
true
"""
@spec valid?(binary()) :: boolean()
def valid?(bic) when is_binary(bic), do: Regex.match?(@bic_regex, bic)
def valid?(bic) when is_binary(bic), do: Regex.match?(bic_regex(), bic)
def valid?(_), do: false

defp presence(""), do: nil
defp presence(val), do: val

defp bic_regex do
~r/^(?<bank>[a-zA-Z]{4})\s*(?<country>[a-zA-Z]{2})\s*(?<location>[0-9a-zA-Z]{2})\s*(?<branch>[0-9a-zA-Z]{3})?$/
end
end

defimpl String.Chars, for: Bankster.Bic do
Expand Down
251 changes: 126 additions & 125 deletions lib/bankster/iban.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,126 +4,6 @@ defmodule Bankster.Iban do
"""

## -- Module constants
# - IBAN Rules
@iban_rules %{
"AA" => %{length: 16, rule: ~r/^[0-9A-Z]{12}$/i},
"AD" => %{length: 24, rule: ~r/^[0-9]{8}[0-9A-Z]{12}$/i},
"AE" => %{length: 23, rule: ~r/^[0-9]{19}$/i},
"AL" => %{length: 28, rule: ~r/^[0-9]{8}[0-9A-Z]{16}$/i},
"AO" => %{length: 25, rule: ~r/^[0-9]{21}$/i},
"AT" => %{length: 20, rule: ~r/^[0-9]{16}$/i},
"AX" => %{length: 18, rule: ~r/^[0-9]{14}$/i},
"AZ" => %{length: 28, rule: ~r/^[A-Z]{4}[0-9A-Z]{20}$/i},
"BA" => %{length: 20, rule: ~r/^[0-9]{16}$/i},
"BE" => %{length: 16, rule: ~r/^[0-9]{12}$/i},
"BF" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"BG" => %{length: 22, rule: ~r/^[A-Z]{4}[0-9]{6}[0-9A-Z]{8}$/i},
"BH" => %{length: 22, rule: ~r/^[A-Z]{4}[0-9]{14}$/i},
"BI" => %{length: 16, rule: ~r/^[0-9]{12}$/i},
"BJ" => %{length: 28, rule: ~r/^[A-Z]{1}[0-9]{23}$/i},
"BL" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"BR" => %{length: 29, rule: ~r/^[0-9]{23}[A-Z]{1}[0-9A-Z]{1}$/i},
"BY" => %{length: 28, rule: ~r/^[0-9A-Z]{4}[0-9]{4}[0-9A-Z]{16}$/i},
"CF" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"CG" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"CH" => %{length: 21, rule: ~r/^[0-9]{5}[0-9A-Z]{12}$/i},
"CI" => %{length: 28, rule: ~r/^[A-Z]{1}[0-9]{23}$/i},
"CM" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"CR" => %{length: 22, rule: ~r/^[0-9]{18}$/i},
"CV" => %{length: 25, rule: ~r/^[0-9]{21}$/i},
"CY" => %{length: 28, rule: ~r/^[0-9]{8}[0-9A-Z]{16}$/i},
"CZ" => %{length: 24, rule: ~r/^[0-9]{20}$/i},
"DE" => %{length: 22, rule: ~r/^[0-9]{18}$/i},
"DJ" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"DK" => %{length: 18, rule: ~r/^[0-9]{14}$/i},
"DO" => %{length: 28, rule: ~r/^[0-9A-Z]{4}[0-9]{20}$/i},
"DZ" => %{length: 24, rule: ~r/^[0-9]{20}$/i},
"EE" => %{length: 20, rule: ~r/^[0-9]{16}$/i},
"EG" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"ES" => %{length: 24, rule: ~r/^[0-9]{20}$/i},
"FI" => %{length: 18, rule: ~r/^[0-9]{14}$/i},
"FO" => %{length: 18, rule: ~r/^[0-9]{14}$/i},
"FR" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"GA" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"GB" => %{length: 22, rule: ~r/^[A-Z]{4}[0-9]{14}$/i},
"GE" => %{length: 22, rule: ~r/^[A-Z]{2}[0-9]{16}$/i},
"GF" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"GI" => %{length: 23, rule: ~r/^[A-Z]{4}[0-9A-Z]{15}$/i},
"GL" => %{length: 18, rule: ~r/^[0-9]{14}$/i},
"GP" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"GQ" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"GR" => %{length: 27, rule: ~r/^[0-9]{7}[0-9A-Z]{16}$/i},
"GT" => %{length: 28, rule: ~r/^[0-9A-Z]{24}$/i},
"GW" => %{length: 25, rule: ~r/^[0-9A-Z]{2}[0-9]{19}$/i},
"HN" => %{length: 28, rule: ~r/^[A-Z]{4}[0-9]{20}$/i},
"HR" => %{length: 21, rule: ~r/^[0-9]{17}$/i},
"HU" => %{length: 28, rule: ~r/^[0-9]{24}$/i},
"IE" => %{length: 22, rule: ~r/^[A-Z]{4}[0-9]{14}$/i},
"IL" => %{length: 23, rule: ~r/^[0-9]{19}$/i},
"IQ" => %{length: 23, rule: ~r/^[0-9A-Z]{4}[0-9]{15}$/i},
"IR" => %{length: 26, rule: ~r/^[0-9]{22}$/i},
"IS" => %{length: 26, rule: ~r/^[0-9]{22}$/i},
"IT" => %{length: 27, rule: ~r/^[A-Z]{1}[0-9]{10}[0-9A-Z]{12}$/i},
"JO" => %{length: 30, rule: ~r/^[A-Z]{4}[0-9]{4}[0-9A-Z]{18}$/i},
"KM" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"KW" => %{length: 30, rule: ~r/^[A-Z]{4}[0-9]{22}$/i},
"KZ" => %{length: 20, rule: ~r/^[0-9]{3}[0-9A-Z]{13}$/i},
"LB" => %{length: 28, rule: ~r/^[0-9]{4}[0-9A-Z]{20}$/i},
"LC" => %{length: 32, rule: ~r/^[A-Z]{4}[0-9A-Z]{24}$/i},
"LI" => %{length: 21, rule: ~r/^[0-9]{5}[0-9A-Z]{12}$/i},
"LT" => %{length: 20, rule: ~r/^[0-9]{16}$/i},
"LU" => %{length: 20, rule: ~r/^[0-9]{3}[0-9A-Z]{13}$/i},
"LV" => %{length: 21, rule: ~r/^[A-Z]{4}[0-9A-Z]{13}$/i},
"MA" => %{length: 28, rule: ~r/^[0-9]{24}$/i},
"MC" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"MD" => %{length: 24, rule: ~r/^[0-9A-Z]{20}$/i},
"ME" => %{length: 22, rule: ~r/^[0-9]{18}$/i},
"MF" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"MG" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"MK" => %{length: 19, rule: ~r/^[0-9]{3}[0-9A-Z]{10}[0-9]{2}$/i},
"ML" => %{length: 28, rule: ~r/^[A-Z]{1}[0-9]{23}$/i},
"MQ" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"MR" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"MT" => %{length: 31, rule: ~r/^[A-Z]{4}[0-9]{5}[0-9A-Z]{18}$/i},
"MU" => %{length: 30, rule: ~r/^[A-Z]{4}[0-9]{19}[A-Z]{3}$/i},
"MZ" => %{length: 25, rule: ~r/^[0-9]{21}$/i},
"NC" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"NE" => %{length: 28, rule: ~r/^[A-Z]{2}[0-9]{22}$/i},
"NI" => %{length: 32, rule: ~r/^[A-Z]{4}[0-9]{24}$/i},
"NL" => %{length: 18, rule: ~r/^[A-Z]{4}[0-9]{10}$/i},
"NO" => %{length: 15, rule: ~r/^[0-9]{11}$/i},
"PF" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"PK" => %{length: 24, rule: ~r/^[A-Z]{4}[0-9A-Z]{16}$/i},
"PL" => %{length: 28, rule: ~r/^[0-9]{24}$/i},
"PM" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"PS" => %{length: 29, rule: ~r/^[A-Z]{4}[0-9A-Z]{21}$/i},
"PT" => %{length: 25, rule: ~r/^[0-9]{21}$/i},
"QA" => %{length: 29, rule: ~r/^[A-Z]{4}[0-9]{4}[0-9A-Z]{17}$/i},
"RE" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"RO" => %{length: 24, rule: ~r/^[A-Z]{4}[0-9A-Z]{16}$/i},
"RS" => %{length: 22, rule: ~r/^[0-9]{18}$/i},
"SA" => %{length: 24, rule: ~r/^[0-9]{2}[0-9A-Z]{18}$/i},
"SC" => %{length: 31, rule: ~r/^[A-Z]{4}[0-9]{20}[A-Z]{3}$/i},
"SE" => %{length: 24, rule: ~r/^[0-9]{20}$/i},
"SI" => %{length: 19, rule: ~r/^[0-9]{15}$/i},
"SK" => %{length: 24, rule: ~r/^[0-9]{20}$/i},
"SM" => %{length: 27, rule: ~r/^[A-Z]{1}[0-9]{10}[0-9A-Z]{12}$/i},
"SN" => %{length: 28, rule: ~r/^[A-Z]{1}[0-9]{23}$/i},
"ST" => %{length: 25, rule: ~r/^[0-9]{8}[0-9]{13}$/i},
"SV" => %{length: 28, rule: ~r/^[0-9A-Z]{4}[0-9]{20}$/i},
"TD" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"TF" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"TG" => %{length: 28, rule: ~r/^[A-Z]{2}[0-9]{22}$/i},
"TL" => %{length: 23, rule: ~r/^[0-9]{19}$/i},
"TN" => %{length: 24, rule: ~r/^[0-9]{20}$/i},
"TR" => %{length: 26, rule: ~r/^[0-9]{6}[0-9A-Z]{16}$/i},
"UA" => %{length: 29, rule: ~r/^[0-9]{6}[0-9A-Z]{19}$/i},
"VG" => %{length: 24, rule: ~r/^[A-Z]{4}[0-9]{16}$/i},
"WF" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"XK" => %{length: 20, rule: ~r/^[0-9]{16}$/i},
"YT" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i}
}

# - IBAN replacements
@replacements %{
"A" => "10",
Expand Down Expand Up @@ -216,7 +96,7 @@ defmodule Bankster.Iban do
["SM", "KZ", "SN", "BA", "GA", "KW", "MU", ...]
"""
@spec supported_countries :: list(binary())
def supported_countries, do: Map.keys(@iban_rules)
def supported_countries, do: Map.keys(iban_rules())

@doc """
Validates whether the given country code is within the supported countries.
Expand All @@ -230,7 +110,7 @@ defmodule Bankster.Iban do
"""
@spec supported_country?(binary()) :: boolean()
def supported_country?(country_code) when is_binary(country_code),
do: Map.has_key?(@iban_rules, format_default(country_code))
do: Map.has_key?(iban_rules(), format_default(country_code))

def supported_country?(_country_code),
do: false
Expand Down Expand Up @@ -330,17 +210,17 @@ defmodule Bankster.Iban do
# - Check whether a given IBAN violates the supported countries.
@spec iban_violates_country?(binary()) :: boolean
defp iban_violates_country?(iban),
do: !Map.has_key?(@iban_rules, country_code(iban))
do: !Map.has_key?(iban_rules(), country_code(iban))

# - Check whether a given IBAN violates the required length.
@spec iban_violates_length?(binary()) :: boolean
defp iban_violates_length?(iban),
do: size(iban) != get_in(@iban_rules, [country_code(iban), :length])
do: size(iban) != get_in(iban_rules(), [country_code(iban), :length])

# - Check whether a given IBAN violates the country rules.
@spec iban_violates_country_rule?(binary()) :: boolean
defp iban_violates_country_rule?(iban) do
if iban_rule = get_in(@iban_rules, [country_code(iban), :rule]) do
if iban_rule = get_in(iban_rules(), [country_code(iban), :rule]) do
!Regex.match?(iban_rule, String.slice(format_default(iban), 4..-1//1))
else
false
Expand All @@ -365,4 +245,125 @@ defmodule Bankster.Iban do

calculated_checksum !== checksum(iban)
end

defp iban_rules do
%{
"AA" => %{length: 16, rule: ~r/^[0-9A-Z]{12}$/i},
"AD" => %{length: 24, rule: ~r/^[0-9]{8}[0-9A-Z]{12}$/i},
"AE" => %{length: 23, rule: ~r/^[0-9]{19}$/i},
"AL" => %{length: 28, rule: ~r/^[0-9]{8}[0-9A-Z]{16}$/i},
"AO" => %{length: 25, rule: ~r/^[0-9]{21}$/i},
"AT" => %{length: 20, rule: ~r/^[0-9]{16}$/i},
"AX" => %{length: 18, rule: ~r/^[0-9]{14}$/i},
"AZ" => %{length: 28, rule: ~r/^[A-Z]{4}[0-9A-Z]{20}$/i},
"BA" => %{length: 20, rule: ~r/^[0-9]{16}$/i},
"BE" => %{length: 16, rule: ~r/^[0-9]{12}$/i},
"BF" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"BG" => %{length: 22, rule: ~r/^[A-Z]{4}[0-9]{6}[0-9A-Z]{8}$/i},
"BH" => %{length: 22, rule: ~r/^[A-Z]{4}[0-9]{14}$/i},
"BI" => %{length: 16, rule: ~r/^[0-9]{12}$/i},
"BJ" => %{length: 28, rule: ~r/^[A-Z]{1}[0-9]{23}$/i},
"BL" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"BR" => %{length: 29, rule: ~r/^[0-9]{23}[A-Z]{1}[0-9A-Z]{1}$/i},
"BY" => %{length: 28, rule: ~r/^[0-9A-Z]{4}[0-9]{4}[0-9A-Z]{16}$/i},
"CF" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"CG" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"CH" => %{length: 21, rule: ~r/^[0-9]{5}[0-9A-Z]{12}$/i},
"CI" => %{length: 28, rule: ~r/^[A-Z]{1}[0-9]{23}$/i},
"CM" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"CR" => %{length: 22, rule: ~r/^[0-9]{18}$/i},
"CV" => %{length: 25, rule: ~r/^[0-9]{21}$/i},
"CY" => %{length: 28, rule: ~r/^[0-9]{8}[0-9A-Z]{16}$/i},
"CZ" => %{length: 24, rule: ~r/^[0-9]{20}$/i},
"DE" => %{length: 22, rule: ~r/^[0-9]{18}$/i},
"DJ" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"DK" => %{length: 18, rule: ~r/^[0-9]{14}$/i},
"DO" => %{length: 28, rule: ~r/^[0-9A-Z]{4}[0-9]{20}$/i},
"DZ" => %{length: 24, rule: ~r/^[0-9]{20}$/i},
"EE" => %{length: 20, rule: ~r/^[0-9]{16}$/i},
"EG" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"ES" => %{length: 24, rule: ~r/^[0-9]{20}$/i},
"FI" => %{length: 18, rule: ~r/^[0-9]{14}$/i},
"FO" => %{length: 18, rule: ~r/^[0-9]{14}$/i},
"FR" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"GA" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"GB" => %{length: 22, rule: ~r/^[A-Z]{4}[0-9]{14}$/i},
"GE" => %{length: 22, rule: ~r/^[A-Z]{2}[0-9]{16}$/i},
"GF" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"GI" => %{length: 23, rule: ~r/^[A-Z]{4}[0-9A-Z]{15}$/i},
"GL" => %{length: 18, rule: ~r/^[0-9]{14}$/i},
"GP" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"GQ" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"GR" => %{length: 27, rule: ~r/^[0-9]{7}[0-9A-Z]{16}$/i},
"GT" => %{length: 28, rule: ~r/^[0-9A-Z]{24}$/i},
"GW" => %{length: 25, rule: ~r/^[0-9A-Z]{2}[0-9]{19}$/i},
"HN" => %{length: 28, rule: ~r/^[A-Z]{4}[0-9]{20}$/i},
"HR" => %{length: 21, rule: ~r/^[0-9]{17}$/i},
"HU" => %{length: 28, rule: ~r/^[0-9]{24}$/i},
"IE" => %{length: 22, rule: ~r/^[A-Z]{4}[0-9]{14}$/i},
"IL" => %{length: 23, rule: ~r/^[0-9]{19}$/i},
"IQ" => %{length: 23, rule: ~r/^[0-9A-Z]{4}[0-9]{15}$/i},
"IR" => %{length: 26, rule: ~r/^[0-9]{22}$/i},
"IS" => %{length: 26, rule: ~r/^[0-9]{22}$/i},
"IT" => %{length: 27, rule: ~r/^[A-Z]{1}[0-9]{10}[0-9A-Z]{12}$/i},
"JO" => %{length: 30, rule: ~r/^[A-Z]{4}[0-9]{4}[0-9A-Z]{18}$/i},
"KM" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"KW" => %{length: 30, rule: ~r/^[A-Z]{4}[0-9]{22}$/i},
"KZ" => %{length: 20, rule: ~r/^[0-9]{3}[0-9A-Z]{13}$/i},
"LB" => %{length: 28, rule: ~r/^[0-9]{4}[0-9A-Z]{20}$/i},
"LC" => %{length: 32, rule: ~r/^[A-Z]{4}[0-9A-Z]{24}$/i},
"LI" => %{length: 21, rule: ~r/^[0-9]{5}[0-9A-Z]{12}$/i},
"LT" => %{length: 20, rule: ~r/^[0-9]{16}$/i},
"LU" => %{length: 20, rule: ~r/^[0-9]{3}[0-9A-Z]{13}$/i},
"LV" => %{length: 21, rule: ~r/^[A-Z]{4}[0-9A-Z]{13}$/i},
"MA" => %{length: 28, rule: ~r/^[0-9]{24}$/i},
"MC" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"MD" => %{length: 24, rule: ~r/^[0-9A-Z]{20}$/i},
"ME" => %{length: 22, rule: ~r/^[0-9]{18}$/i},
"MF" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"MG" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"MK" => %{length: 19, rule: ~r/^[0-9]{3}[0-9A-Z]{10}[0-9]{2}$/i},
"ML" => %{length: 28, rule: ~r/^[A-Z]{1}[0-9]{23}$/i},
"MQ" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"MR" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"MT" => %{length: 31, rule: ~r/^[A-Z]{4}[0-9]{5}[0-9A-Z]{18}$/i},
"MU" => %{length: 30, rule: ~r/^[A-Z]{4}[0-9]{19}[A-Z]{3}$/i},
"MZ" => %{length: 25, rule: ~r/^[0-9]{21}$/i},
"NC" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"NE" => %{length: 28, rule: ~r/^[A-Z]{2}[0-9]{22}$/i},
"NI" => %{length: 32, rule: ~r/^[A-Z]{4}[0-9]{24}$/i},
"NL" => %{length: 18, rule: ~r/^[A-Z]{4}[0-9]{10}$/i},
"NO" => %{length: 15, rule: ~r/^[0-9]{11}$/i},
"PF" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"PK" => %{length: 24, rule: ~r/^[A-Z]{4}[0-9A-Z]{16}$/i},
"PL" => %{length: 28, rule: ~r/^[0-9]{24}$/i},
"PM" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"PS" => %{length: 29, rule: ~r/^[A-Z]{4}[0-9A-Z]{21}$/i},
"PT" => %{length: 25, rule: ~r/^[0-9]{21}$/i},
"QA" => %{length: 29, rule: ~r/^[A-Z]{4}[0-9]{4}[0-9A-Z]{17}$/i},
"RE" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"RO" => %{length: 24, rule: ~r/^[A-Z]{4}[0-9A-Z]{16}$/i},
"RS" => %{length: 22, rule: ~r/^[0-9]{18}$/i},
"SA" => %{length: 24, rule: ~r/^[0-9]{2}[0-9A-Z]{18}$/i},
"SC" => %{length: 31, rule: ~r/^[A-Z]{4}[0-9]{20}[A-Z]{3}$/i},
"SE" => %{length: 24, rule: ~r/^[0-9]{20}$/i},
"SI" => %{length: 19, rule: ~r/^[0-9]{15}$/i},
"SK" => %{length: 24, rule: ~r/^[0-9]{20}$/i},
"SM" => %{length: 27, rule: ~r/^[A-Z]{1}[0-9]{10}[0-9A-Z]{12}$/i},
"SN" => %{length: 28, rule: ~r/^[A-Z]{1}[0-9]{23}$/i},
"ST" => %{length: 25, rule: ~r/^[0-9]{8}[0-9]{13}$/i},
"SV" => %{length: 28, rule: ~r/^[0-9A-Z]{4}[0-9]{20}$/i},
"TD" => %{length: 27, rule: ~r/^[0-9]{23}$/i},
"TF" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"TG" => %{length: 28, rule: ~r/^[A-Z]{2}[0-9]{22}$/i},
"TL" => %{length: 23, rule: ~r/^[0-9]{19}$/i},
"TN" => %{length: 24, rule: ~r/^[0-9]{20}$/i},
"TR" => %{length: 26, rule: ~r/^[0-9]{6}[0-9A-Z]{16}$/i},
"UA" => %{length: 29, rule: ~r/^[0-9]{6}[0-9A-Z]{19}$/i},
"VG" => %{length: 24, rule: ~r/^[A-Z]{4}[0-9]{16}$/i},
"WF" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i},
"XK" => %{length: 20, rule: ~r/^[0-9]{16}$/i},
"YT" => %{length: 27, rule: ~r/^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$/i}
}
end
end