2024-03-05 11:02:58 +00:00
|
|
|
defmodule IbanEx.Parser do
|
2024-03-07 23:39:33 +00:00
|
|
|
@moduledoc false
|
|
|
|
|
2024-03-05 11:02:58 +00:00
|
|
|
alias IbanEx.{Country, Iban, Validator}
|
2024-05-15 03:45:34 +00:00
|
|
|
import IbanEx.Commons, only: [normalize_and_slice: 2]
|
2024-03-05 11:02:58 +00:00
|
|
|
|
|
|
|
@type iban_string() :: String.t()
|
|
|
|
@type country_code_string() :: <<_::16>>
|
|
|
|
@type check_digits_string() :: <<_::16>>
|
|
|
|
|
2024-05-14 23:15:55 +00:00
|
|
|
@type iban() :: IbanEx.Iban.t()
|
|
|
|
@type iban_or_error() ::
|
|
|
|
{:ok, iban()}
|
|
|
|
| {:invalid_checksum, binary()}
|
|
|
|
| {:invalid_format, binary()}
|
|
|
|
| {:invalid_length, binary()}
|
|
|
|
| {:can_not_parse_map, binary()}
|
|
|
|
| {:unsupported_country_code, binary()}
|
|
|
|
|
|
|
|
@spec parse({:ok, binary()}) :: iban_or_error()
|
2024-03-05 11:02:58 +00:00
|
|
|
def parse({:ok, iban_string}), do: parse(iban_string)
|
2024-03-07 23:39:33 +00:00
|
|
|
|
2024-05-16 08:55:21 +00:00
|
|
|
def parse(iban_string, options \\ [incomplete: false])
|
|
|
|
|
|
|
|
def parse(iban_string, incomplete: false) do
|
2024-03-07 23:39:33 +00:00
|
|
|
case Validator.validate(iban_string) do
|
|
|
|
{:ok, valid_iban} ->
|
|
|
|
iban_map = %{
|
|
|
|
country_code: country_code(valid_iban),
|
|
|
|
check_digits: check_digits(valid_iban)
|
|
|
|
}
|
|
|
|
|
|
|
|
bban_map =
|
2024-05-11 15:52:34 +00:00
|
|
|
iban_string
|
|
|
|
|> bban()
|
|
|
|
|> parse_bban(iban_map.country_code)
|
2024-03-07 23:39:33 +00:00
|
|
|
|
|
|
|
{:ok, struct(Iban, Map.merge(iban_map, bban_map))}
|
|
|
|
|
|
|
|
{:error, error_type} ->
|
|
|
|
{:error, error_type}
|
2024-03-05 11:02:58 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-05-16 08:55:21 +00:00
|
|
|
def parse(iban_string, incomplete: true) do
|
|
|
|
iban_map = %{
|
|
|
|
country_code: country_code(iban_string),
|
|
|
|
check_digits: check_digits(iban_string)
|
|
|
|
}
|
|
|
|
|
|
|
|
bban = bban(iban_string)
|
|
|
|
|
|
|
|
case Country.is_country_code_supported?(iban_map.country_code) do
|
|
|
|
true ->
|
|
|
|
result =
|
|
|
|
parse_bban(bban, iban_map.country_code, incomplete: true)
|
|
|
|
|> Map.merge(iban_map)
|
|
|
|
|
|
|
|
{:ok, struct(Iban, result)}
|
|
|
|
|
|
|
|
false ->
|
|
|
|
{:error, :unsupported_country_code}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-05-11 15:52:34 +00:00
|
|
|
@spec parse_bban(binary(), <<_::16>>) :: map()
|
2024-05-15 03:09:24 +00:00
|
|
|
def parse_bban(bban_string, country_code, options \\ [incomplete: false])
|
|
|
|
|
|
|
|
def parse_bban(bban_string, country_code, incomplete: true) do
|
2024-05-15 04:04:47 +00:00
|
|
|
case Country.is_country_code_supported?(country_code) do
|
|
|
|
true ->
|
2024-05-16 19:01:17 +00:00
|
|
|
country_code
|
|
|
|
|> Country.country_module()
|
|
|
|
|> parse_bban_by_country_rules(bban_string)
|
2024-05-15 04:04:47 +00:00
|
|
|
false ->
|
|
|
|
%{}
|
|
|
|
end
|
2024-05-15 03:09:24 +00:00
|
|
|
end
|
|
|
|
|
2024-05-15 04:04:47 +00:00
|
|
|
def parse_bban(bban_string, country_code, incomplete: false) do
|
|
|
|
case Country.is_country_code_supported?(country_code) do
|
|
|
|
true ->
|
|
|
|
Country.country_module(country_code).rule()
|
|
|
|
|> parse_bban_by_regex(bban_string)
|
|
|
|
false ->
|
|
|
|
%{}
|
|
|
|
end
|
2024-05-15 03:09:24 +00:00
|
|
|
end
|
|
|
|
|
2024-05-16 19:01:17 +00:00
|
|
|
defp parse_bban_by_country_rules(country_module, bban_string) do
|
2024-05-16 08:55:21 +00:00
|
|
|
for {field, rule} <- country_module.rules,
|
|
|
|
into: %{},
|
|
|
|
do: {field, normalize_and_slice(bban_string, rule.range)}
|
|
|
|
end
|
|
|
|
|
2024-05-15 03:22:16 +00:00
|
|
|
defp parse_bban_by_regex(_regex, nil), do: %{}
|
2024-05-16 08:55:21 +00:00
|
|
|
|
2024-05-15 03:09:24 +00:00
|
|
|
defp parse_bban_by_regex(regex, bban_string) do
|
|
|
|
case Regex.named_captures(regex, bban_string) do
|
|
|
|
map when is_map(map) ->
|
|
|
|
for {key, val} <- map,
|
|
|
|
into: %{},
|
2024-05-15 03:45:34 +00:00
|
|
|
do: {String.to_atom(key), val}
|
2024-05-15 03:09:24 +00:00
|
|
|
|
|
|
|
nil ->
|
|
|
|
%{}
|
|
|
|
end
|
2024-05-11 15:52:34 +00:00
|
|
|
end
|
|
|
|
|
2024-03-05 11:02:58 +00:00
|
|
|
@spec country_code(iban_string()) :: country_code_string()
|
2024-05-15 03:45:34 +00:00
|
|
|
def country_code(iban_string), do: normalize_and_slice(iban_string, 0..1)
|
2024-03-05 11:02:58 +00:00
|
|
|
|
|
|
|
@spec check_digits(binary()) :: check_digits_string()
|
2024-05-15 03:45:34 +00:00
|
|
|
def check_digits(iban_string), do: normalize_and_slice(iban_string, 2..3)
|
2024-03-05 11:02:58 +00:00
|
|
|
|
|
|
|
@spec bban(binary()) :: binary()
|
2024-05-15 03:45:34 +00:00
|
|
|
def bban(iban_string), do: normalize_and_slice(iban_string, 4..-1//1)
|
2024-03-05 11:02:58 +00:00
|
|
|
end
|