diff --git a/lib/ex_phone_number/extraction.ex b/lib/ex_phone_number/extraction.ex index fefa832..878f6e1 100644 --- a/lib/ex_phone_number/extraction.ex +++ b/lib/ex_phone_number/extraction.ex @@ -232,4 +232,38 @@ defmodule ExPhoneNumber.Extraction do {false, number} end end + + @doc """ + Attempts to extract a valid number from a phone number that is too long to be + valid, and resets the PhoneNumber object passed in to that valid version. If + no valid number could be extracted, the PhoneNumber object passed in will not + be modified. + + Implements `i18n.phonenumbers.PhoneNumberUtil.prototype.truncateTooLongNumber` + """ + @spec truncate_too_long_number(%PhoneNumber{}) :: {boolean(), %PhoneNumber{}} + def truncate_too_long_number(%PhoneNumber{} = phone_number) do + if is_valid_number?(phone_number) do + {true, phone_number} + else + national_number = PhoneNumber.get_national_number_or_default(phone_number) + truncate_too_long_number(phone_number, national_number) + end + end + + defp truncate_too_long_number(%PhoneNumber{} = phone_number, national_number) do + national_number = div(national_number, 10) + phone_number_copy = %PhoneNumber{phone_number | national_number: national_number} + + cond do + national_number == 0 or is_possible_number_with_reason?(phone_number_copy) == ValidationResults.too_short() -> + {false, phone_number} + + not is_valid_number?(phone_number_copy) -> + truncate_too_long_number(phone_number, national_number) + + true -> + {true, phone_number_copy} + end + end end diff --git a/lib/ex_phone_number/model/phone_number.ex b/lib/ex_phone_number/model/phone_number.ex index c724807..483906b 100644 --- a/lib/ex_phone_number/model/phone_number.ex +++ b/lib/ex_phone_number/model/phone_number.ex @@ -32,6 +32,15 @@ defmodule ExPhoneNumber.Model.PhoneNumber do not is_nil(phone_number.country_code) end + @national_number_default 0 + def get_national_number_or_default(phone_number = %PhoneNumber{}) do + if is_nil(phone_number.national_number) do + @national_number_default + else + phone_number.national_number + end + end + @spec has_national_number?(%PhoneNumber{}) :: boolean() def has_national_number?(phone_number = %PhoneNumber{}) do not is_nil(phone_number.national_number) diff --git a/test/ex_phone_number/extraction_test.exs b/test/ex_phone_number/extraction_test.exs index bd71363..8d2a1c3 100644 --- a/test/ex_phone_number/extraction_test.exs +++ b/test/ex_phone_number/extraction_test.exs @@ -6,6 +6,7 @@ defmodule ExPhoneNumber.ExtractionTest do import ExPhoneNumber.Extraction alias ExPhoneNumber.Metadata + alias ExPhoneNumber.PhoneNumberFixture alias ExPhoneNumber.RegionCodeFixture alias ExPhoneNumber.Constants.CountryCodeSource alias ExPhoneNumber.Constants.ErrorMessages @@ -233,4 +234,22 @@ defmodule ExPhoneNumber.ExtractionTest do assert CountryCodeSource.from_default_country() == phone_number.country_code_source end end + + describe ".truncate_too_long_number/1" do + test "TruncateTooLongNumber" do + assert {true, PhoneNumberFixture.gb_toll_free()} == truncate_too_long_number(PhoneNumberFixture.gb_toll_free_too_long()) + + assert {true, PhoneNumberFixture.it_number2()} == truncate_too_long_number(PhoneNumberFixture.it_number2_too_long()) + + assert {true, PhoneNumberFixture.us_number()} == truncate_too_long_number(PhoneNumberFixture.us_long_number()) + + assert {true, PhoneNumberFixture.international_toll_free()} == truncate_too_long_number(PhoneNumberFixture.international_toll_free_too_long()) + + assert {true, PhoneNumberFixture.gb_toll_free()} == truncate_too_long_number(PhoneNumberFixture.gb_toll_free()) + + assert {false, PhoneNumberFixture.us_invalid_prefix()} == truncate_too_long_number(PhoneNumberFixture.us_invalid_prefix()) + + assert {false, PhoneNumberFixture.us_short_number()} == truncate_too_long_number(PhoneNumberFixture.us_short_number()) + end + end end diff --git a/test/support/phone_number_fixture.ex b/test/support/phone_number_fixture.ex index a173404..3a48c4f 100644 --- a/test/support/phone_number_fixture.ex +++ b/test/support/phone_number_fixture.ex @@ -23,7 +23,7 @@ defmodule ExPhoneNumber.PhoneNumberFixture do def ad_number() do %PhoneNumber{ country_code: 376, - national_number: 12345 + national_number: 12_345 } end @@ -291,6 +291,13 @@ defmodule ExPhoneNumber.PhoneNumberFixture do } end + def gb_toll_free_too_long() do + %PhoneNumber{ + country_code: 44, + national_number: 80_123_456_780_123 + } + end + def gb_shared_cost() do %PhoneNumber{ country_code: 44, @@ -357,6 +364,22 @@ defmodule ExPhoneNumber.PhoneNumberFixture do } end + def it_number2() do + %PhoneNumber{ + country_code: 39, + national_number: 2_234_567_890, + italian_leading_zero: true + } + end + + def it_number2_too_long() do + %PhoneNumber{ + country_code: 39, + national_number: 2_234_567_890_123, + italian_leading_zero: true + } + end + def it_premium() do %PhoneNumber{ country_code: 39, @@ -598,6 +621,13 @@ defmodule ExPhoneNumber.PhoneNumberFixture do } end + def us_short_number() do + %PhoneNumber{ + country_code: 1, + national_number: 1_234 + } + end + def us_short_by_one_number() do %PhoneNumber{ country_code: 1, @@ -658,6 +688,13 @@ defmodule ExPhoneNumber.PhoneNumberFixture do } end + def us_invalid_prefix() do + %PhoneNumber{ + country_code: 1, + national_number: 2_401_234_567 + } + end + def uz_fixed_line() do %PhoneNumber{ country_code: 998,