Skip to content

Commit

Permalink
Update XML metadata to the last version, and code accordingly (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
JerryBels authored and szymon-jez committed Feb 4, 2019
1 parent 4543a7b commit 9fa3474
Show file tree
Hide file tree
Showing 13 changed files with 15,358 additions and 12,031 deletions.
2 changes: 2 additions & 0 deletions lib/ex_phone_number/constants/validation_results.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ defmodule ExPhoneNumber.Constants.ValidationResults do

def too_short(), do: :too_short

def invalid_length(), do: :invalid_length

def too_long(), do: :too_long
end
2 changes: 2 additions & 0 deletions lib/ex_phone_number/constants/values.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,7 @@ defmodule ExPhoneNumber.Constants.Values do

def description_default_pattern(), do: "NA"

def description_default_length(), do: [-1]

def default_extn_prefix(), do: " ext. "
end
2 changes: 1 addition & 1 deletion lib/ex_phone_number/extraction.ex
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ defmodule ExPhoneNumber.Extraction do
potential_national_number = if result, do: possible_national_number, else: potential_national_number
if (not matches_entirely?(metadata.general.national_number_pattern, full_number) and
matches_entirely?(metadata.general.national_number_pattern, potential_national_number))
or test_number_length_against_pattern(metadata.general.possible_number_pattern, full_number) == ValidationResults.too_long do
or test_number_length(full_number, metadata) == ValidationResults.too_long do
phone_number = if keep_raw_input, do: %{phone_number | country_code: metadata.country_code, country_code_source: CountryCodeSource.from_number_without_plus_sign},
else: phone_number
{true, potential_national_number, %{phone_number | country_code: metadata.country_code}}
Expand Down
16 changes: 10 additions & 6 deletions lib/ex_phone_number/metadata.ex
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,20 @@ defmodule ExPhoneNumber.Metadata do
end
end

def get_region_code_for_number_from_region_list(%PhoneNumber{} = phone_number, region_codes) when is_list(region_codes) do
defp get_region_code_for_number_from_region_list(%PhoneNumber{} = phone_number, region_codes) when is_list(region_codes) do
region_codes = if_gb_regions_ensure_gb_first(region_codes)
national_number = PhoneNumber.get_national_significant_number(phone_number)
find_matching_region_code(region_codes, national_number)
end

# Ensure `GB` is first when checking numbers that match `country_code: 44`. In the Javascript official library it's the case.
defp if_gb_regions_ensure_gb_first(regions) do
case Enum.member?(regions, "GB") do
false -> regions
true -> Enum.sort(regions)
end
end

def get_region_codes_for_country_code(country_code) when is_number(country_code) do
List.wrap(country_code_to_region_code_map()[country_code])
end
Expand All @@ -160,11 +169,6 @@ defmodule ExPhoneNumber.Metadata do
end)
end

def is_leading_zero_possible?(country_code) when is_number(country_code) do
metadata = get_for_region_code_or_calling_code(country_code, get_region_code_for_country_code(country_code))
not is_nil(metadata) and PhoneMetadata.get_leading_zero_possible_or_default(metadata)
end

def is_nanpa_country?(nil), do: false
def is_nanpa_country?(region_code) when is_binary(region_code) do
String.upcase(region_code) in country_code_to_region_code_map()[Values.nanpa_country_code]
Expand Down
120 changes: 72 additions & 48 deletions lib/ex_phone_number/metadata/phone_metadata.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ defmodule ExPhoneNumber.Metadata.PhoneMetadata do
national_prefix_optional_when_formatting: nil, # string
preferred_extn_prefix: nil, # string
main_country_for_code: nil, # boolean
leading_zero_possible: nil, # boolean
mobile_number_portable_region: nil, # boolean
carrier_code_formatting_rule: nil, # string
general: nil, # %PhoneNumberDescription{}
Expand Down Expand Up @@ -43,41 +42,75 @@ defmodule ExPhoneNumber.Metadata.PhoneMetadata do

def from_xpath_node(xpath_node) do
kwlist =
xpath_node |> xmap(
id: ~x"./@id"s,
country_code: ~x"./@countryCode"i,
leading_digits: ~x"./@leadingDigits"o |> transform_by(&normalize_pattern/1),
international_prefix: ~x"./@internationalPrefix"s,
preferred_international_prefix: ~x"./@preferredInternationalPrefix"o |> transform_by(&normalize_string/1),
national_prefix: ~x"./@nationalPrefix"s,
national_prefix_for_parsing: ~x"./@nationalPrefixForParsing"o |> transform_by(&normalize_string/1),
national_prefix_transform_rule: ~x"./@nationalPrefixTransformRule"o |> transform_by(&normalize_rule/1),
national_prefix_formatting_rule: ~x"./@nationalPrefixFormattingRule"s,
preferred_extn_prefix: ~x"./@preferredExtnPrefix"o |> transform_by(&normalize_string/1),
main_country_for_code: ~x"./@mainCountryForCode"o |> transform_by(&normalize_boolean/1),
leading_zero_possible: ~x"./@leadingZeroPossible"o |> transform_by(&normalize_boolean/1),
mobile_number_portable_region: ~x"./@mobileNumberPortableRegion"o |> transform_by(&normalize_boolean/1),
carrier_code_formatting_rule: ~x"./@carrierCodeFormattingRule"s,
general: ~x"./generalDesc"e |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
fixed_line: ~x"./fixedLine"e |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
mobile: ~x"./mobile"e |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
toll_free: ~x"./tollFree"o |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
premium_rate: ~x"./premiumRate"e |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
shared_cost: ~x"./sharedCost"e |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
personal_number: ~x"./personalNumber"e |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
voip: ~x"./voip"e |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
pager: ~x"./pager"e |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
uan: ~x"./uan"e |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
voicemail: ~x"./voicemail"e |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
no_international_dialing: ~x"./noInternationalDialling"e |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
available_formats: [
~x"./availableFormats/numberFormat"el,
number_format: ~x"."e |> transform_by(&NumberFormat.from_xpath_node/1)
]
)
xpath_node
|> xmap(
id: ~x"./@id"s,
country_code: ~x"./@countryCode"i,
leading_digits: ~x"./@leadingDigits"o |> transform_by(&normalize_pattern/1),
international_prefix: ~x"./@internationalPrefix"s,
preferred_international_prefix: ~x"./@preferredInternationalPrefix"o |> transform_by(&normalize_string/1),
national_prefix: ~x"./@nationalPrefix"s,
national_prefix_for_parsing: ~x"./@nationalPrefixForParsing"o |> transform_by(&normalize_string/1),
national_prefix_transform_rule: ~x"./@nationalPrefixTransformRule"o |> transform_by(&normalize_rule/1),
national_prefix_formatting_rule: ~x"./@nationalPrefixFormattingRule"s,
preferred_extn_prefix: ~x"./@preferredExtnPrefix"o |> transform_by(&normalize_string/1),
main_country_for_code: ~x"./@mainCountryForCode"o |> transform_by(&normalize_boolean/1),
mobile_number_portable_region: ~x"./@mobileNumberPortableRegion"o |> transform_by(&normalize_boolean/1),
carrier_code_formatting_rule: ~x"./@carrierCodeFormattingRule"s,
general: ~x"./generalDesc"e |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
fixed_line: ~x"./fixedLine"e |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
mobile: ~x"./mobile"e |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
toll_free: ~x"./tollFree"o |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
premium_rate: ~x"./premiumRate"e |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
shared_cost: ~x"./sharedCost"e |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
personal_number: ~x"./personalNumber"e |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
voip: ~x"./voip"e |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
pager: ~x"./pager"e |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
uan: ~x"./uan"e |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
voicemail: ~x"./voicemail"e |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
no_international_dialing: ~x"./noInternationalDialling"e |> transform_by(&PhoneNumberDescription.from_xpath_node/1),
available_formats: [
~x"./availableFormats/numberFormat"el,
number_format: ~x"."e |> transform_by(&NumberFormat.from_xpath_node/1)
]
)

kwlist = if(is_map(kwlist) && is_map(kwlist.general)) do
put_in(kwlist.general.possible_lengths, general_possible_lengths(kwlist))
else
kwlist
end

struct(%PhoneMetadata{}, kwlist)
end

defp general_possible_lengths(kwlist) do
[
:fixed_line,
:mobile,
:toll_free,
:premium_rate,
:shared_cost,
:personal_number,
:voip,
:pager,
:uan,
:voicemail,
:no_international_dialing,
]
|> Enum.flat_map(fn type ->
kwlist
|> Map.get(type)
|> get_possible_length()
end)
|> Enum.reject(&is_nil/1)
|> Enum.sort
|> Enum.uniq
end

defp get_possible_length(nil), do: []
defp get_possible_length(phone_number_description = %PhoneNumberDescription{}), do: phone_number_description.possible_lengths || []

defp normalize_rule(nil), do: nil
defp normalize_rule(char_list) when is_list(char_list), do: char_list |> List.to_string() |> normalize_rule()
defp normalize_rule(string) when is_binary(string) do
Expand Down Expand Up @@ -134,15 +167,6 @@ defmodule ExPhoneNumber.Metadata.PhoneMetadata do
end
end

@leading_zero_possible_default false
def get_leading_zero_possible_or_default(%PhoneMetadata{} = phone_metadata) do
if is_nil(phone_metadata.leading_zero_possible) do
@leading_zero_possible_default
else
phone_metadata.leading_zero_possible
end
end

@mobile_number_portable_region_default false
def get_mobile_number_portable_region_or_default(phone_metadata = %PhoneMetadata{}) do
if is_nil(phone_metadata.mobile_number_portable_region) do
Expand Down Expand Up @@ -321,11 +345,11 @@ defmodule ExPhoneNumber.Metadata.PhoneMetadata do
else
description.national_number_pattern
end
possible_number_pattern =
if is_nil_or_empty?(description.possible_number_pattern) do
general.possible_number_pattern
possible_lengths =
if is_nil_or_empty?(description.possible_lengths) do
general.possible_lengths
else
description.possible_number_pattern
description.possible_lengths
end
example_number =
if is_nil_or_empty?(description.example_number) do
Expand All @@ -335,14 +359,14 @@ defmodule ExPhoneNumber.Metadata.PhoneMetadata do
end
%PhoneNumberDescription{
national_number_pattern: national_number_pattern,
possible_number_pattern: possible_number_pattern,
possible_lengths: possible_lengths,
example_number: example_number
}
end

def process_other_phone_number_description(description, %PhoneMetadata{general: general}) do
if is_nil(description) do
%PhoneNumberDescription{national_number_pattern: Values.description_default_pattern, possible_number_pattern: Values.description_default_pattern}
%PhoneNumberDescription{national_number_pattern: Values.description_default_pattern, possible_lengths: Values.description_default_length}
else
process_phone_number_description(description, general)
end
Expand Down
63 changes: 51 additions & 12 deletions lib/ex_phone_number/metadata/phone_number_description.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
defmodule ExPhoneNumber.Metadata.PhoneNumberDescription do
defstruct national_number_pattern: nil, # string
possible_number_pattern: nil, # string
possible_lengths: nil, # list
example_number: nil # string

import SweetXml
Expand All @@ -9,28 +9,67 @@ defmodule ExPhoneNumber.Metadata.PhoneNumberDescription do
def from_xpath_node(nil), do: nil
def from_xpath_node(xpath_node) do
kwlist =
xpath_node |> xmap(
xmap(
xpath_node,
national_number_pattern: ~x"./nationalNumberPattern/text()"o |> transform_by(&normalize_pattern/1),
possible_number_pattern: ~x"./possibleNumberPattern/text()"o |> transform_by(&normalize_pattern/1),
national_possible_lengths: ~x"./possibleLengths/@national"o |> transform_by(&normalize_range/1),
local_possible_lengths: ~x"./possibleLengths/@localOnly"o |> transform_by(&normalize_range/1),
example_number: ~x"./exampleNumber/text()"o |> transform_by(&normalize_string/1)
)
struct(%PhoneNumberDescription{}, kwlist)

possible_lengths =
(kwlist.local_possible_lengths || [])
|> Enum.concat(kwlist.national_possible_lengths || [])
|> Enum.sort
|> Enum.uniq

struct(%PhoneNumberDescription{}, %{
national_number_pattern: kwlist.national_number_pattern,
possible_lengths: possible_lengths,
example_number: kwlist.example_number
})
end

defp normalize_string(nil), do: nil
defp normalize_string(char_list) when is_list(char_list) do
char_list
|> List.to_string()
defp clean_string(string) when is_binary(string) do
string
|> String.split(["\n", " "], trim: true)
|> List.to_string()
|> List.to_string
end

defp normalize_string(nil), do: nil
defp normalize_string(char_list) when is_list(char_list), do: List.to_string(char_list) |> clean_string

defp normalize_pattern(nil), do: nil
defp normalize_pattern(char_list) when is_list(char_list) do
char_list
|> List.to_string()
|> String.split(["\n", " "], trim: true)
|> List.to_string()
|> List.to_string
|> clean_string
|> Regex.compile!()
end

defp normalize_range(nil), do: nil
defp normalize_range(char_list) when is_list(char_list) do
char_list
|> List.to_string
|> clean_string
|> String.split(",")
|> Enum.map(&range_to_list/1)
|> List.flatten
|> Enum.sort
|> Enum.uniq
end

defp range_to_list(range_or_number) do
case String.first(range_or_number) do
"[" ->
[range_start, range_end] =
String.slice(range_or_number, 1, String.length(range_or_number) - 2)
|> String.split("-")
|> Enum.map(fn n -> String.to_integer(n) end)

Range.new(range_start, range_end)
|> Enum.to_list
_ -> String.to_integer(range_or_number)
end
end
end
5 changes: 2 additions & 3 deletions lib/ex_phone_number/utilities.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@ defmodule ExPhoneNumber.Utilities do
def is_nil_or_empty?(_), do: false

def is_number_matching_description?(number, %PhoneNumberDescription{} = description) when is_binary(number) do
if description.possible_number_pattern == Values.description_default_pattern or description.national_number_pattern == Values.description_default_pattern do
if description.possible_lengths == Values.description_default_length or description.national_number_pattern == Values.description_default_pattern do
false
else
matches_entirely?(description.possible_number_pattern, number) and
matches_entirely?(description.national_number_pattern, number)
matches_entirely?(description.national_number_pattern, number)
end
end

Expand Down
Loading

0 comments on commit 9fa3474

Please sign in to comment.