diff --git a/lib/bankster/bic.ex b/lib/bankster/bic.ex index ccfcf3a..753d3ea 100644 --- a/lib/bankster/bic.ex +++ b/lib/bankster/bic.ex @@ -14,8 +14,6 @@ defmodule Bankster.Bic do branch: binary() | nil } - @bic_regex ~r/^(?[a-zA-Z]{4})\s*(?[a-zA-Z]{2})\s*(?[0-9a-zA-Z]{2})\s*(?[0-9a-zA-Z]{3})?$/ - @doc """ Parses a BIC string into its components. @@ -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 @@ -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/^(?[a-zA-Z]{4})\s*(?[a-zA-Z]{2})\s*(?[0-9a-zA-Z]{2})\s*(?[0-9a-zA-Z]{3})?$/ + end end defimpl String.Chars, for: Bankster.Bic do diff --git a/lib/bankster/iban.ex b/lib/bankster/iban.ex index 0666ecf..c540e3e 100644 --- a/lib/bankster/iban.ex +++ b/lib/bankster/iban.ex @@ -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", @@ -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. @@ -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 @@ -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 @@ -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