From 7e09cf22491c7426b2bee3a53c0b3fd7a5201058 Mon Sep 17 00:00:00 2001 From: Trevor Pilley Date: Sat, 6 May 2023 19:09:40 +0000 Subject: [PATCH] closes #473 Simplify the parse methods by using optional parameters for ParseOptions --- src/PhoneNumbers/PhoneNumber.cs | 181 ++++++-------------- test/PhoneNumbers.Tests/PhoneNumberTests.cs | 73 +++++--- 2 files changed, 102 insertions(+), 152 deletions(-) diff --git a/src/PhoneNumbers/PhoneNumber.cs b/src/PhoneNumbers/PhoneNumber.cs index 623703d8b..5eaaa8e43 100644 --- a/src/PhoneNumbers/PhoneNumber.cs +++ b/src/PhoneNumbers/PhoneNumber.cs @@ -57,28 +57,15 @@ protected PhoneNumber(PhoneNumberHint phoneNumberHint) protected PhoneNumberHint Hint { get; } /// - /// Parses the specified phone number value into a instance based upon its calling code using the default . + /// Parses the specified phone number value into a instance based upon its calling code. /// /// A string containing a phone number in international format (e.g. +XX). + /// The to use for parsing the phone number, if not specified (or explicitly set to null) the default parse options are used. /// Thrown if the value cannot be successfully parsed into a . /// A instance representing the specified phone number string value. - public static PhoneNumber Parse(string value) => - Parse(value, ParseOptions.Default); - - /// - /// Parses the specified phone number value into a instance based upon its calling code using the specified . - /// - /// A string containing a phone number in international format (e.g. +XX). - /// The options for parsing the phone number. - /// Thrown if the specified is null. - /// Thrown if the value cannot be successfully parsed into a . - /// A instance representing the specified phone number string value. - public static PhoneNumber Parse(string value, ParseOptions options) + public static PhoneNumber Parse(string value, ParseOptions? options = null) { - if (options is null) - { - throw new ArgumentNullException(nameof(options)); - } + options = options ?? ParseOptions.Default; foreach (var countryInfo in options.GetCountryInfos(value)) { @@ -90,50 +77,26 @@ public static PhoneNumber Parse(string value, ParseOptions options) } } - throw new ParseException("Parse(value) only supports a value starting with a supported international calling code (e.g. +44), otherwise Parse(value, countryCode) must be used."); + throw new ParseException($"The value '{value}' could not be successfully parsed into a phone number for any country enabled in ParseOptions."); } /// - /// Parses the specified phone number value into a instance for the specified using the default . + /// Parses the specified phone number value into a instance for the specified . /// - /// A string containing a phone number. + /// A string containing a phone number in the international (e.g. +XX) or national format for the given . /// The of the country for the phone number. + /// The to use for parsing the phone number, if not specified (or explicitly set to null) the default parse options are used. /// Thrown if the specified is null. /// Thrown if the value cannot be successfully parsed into a . /// A instance representing the specified phone number string value. - public static PhoneNumber Parse(string value, CountryInfo countryInfo) => - Parse(value, countryInfo, ParseOptions.Default); - - /// - /// Parses the specified phone number value into a instance for the given ISO 3166 Alpha-2 country code using the default . - /// - /// A string containing a phone number. - /// The ISO 3166 Alpha-2 country code of the country for the phone number. - /// Thrown if the value cannot be successfully parsed into a . - /// A instance representing the specified phone number string value. - public static PhoneNumber Parse(string value, string countryCode) => - Parse(value, countryCode, ParseOptions.Default); - - /// - /// Parses the specified phone number value into a instance for the specified using the specified . - /// - /// A string containing a phone number. - /// The of the country for the phone number. - /// The options for parsing the phone number. - /// Thrown if the specified or are null. - /// Thrown if the value cannot be successfully parsed into a . - /// A instance representing the specified phone number string value. - public static PhoneNumber Parse(string value, CountryInfo countryInfo, ParseOptions options) + public static PhoneNumber Parse(string value, CountryInfo countryInfo, ParseOptions? options = null) { if (countryInfo is null) { throw new ArgumentNullException(nameof(countryInfo)); } - if (options is null) - { - throw new ArgumentNullException(nameof(options)); - } + options = options ?? ParseOptions.Default; if (!options.Countries.Contains(countryInfo)) { @@ -147,26 +110,22 @@ public static PhoneNumber Parse(string value, CountryInfo countryInfo, ParseOpti } /// - /// Parses the specified phone number value into a instance for the given ISO 3166 Alpha-2 country code using the specified . + /// Parses the specified phone number value into a instance for the given ISO 3166 Alpha-2 country code. /// - /// A string containing a phone number. + /// A string containing a phone number in the international (e.g. +XX) or national format for the given ISO 3166 Alpha-2 country code. /// The ISO 3166 Alpha-2 country code of the country for the phone number. - /// The options for parsing the phone number. - /// Thrown if the specified is null. + /// The to use for parsing the phone number, if not specified (or explicitly set to null) the default parse options are used. /// Thrown if the value cannot be successfully parsed into a . /// A instance representing the specified phone number string value. - public static PhoneNumber Parse(string value, string countryCode, ParseOptions options) + public static PhoneNumber Parse(string value, string countryCode, ParseOptions? options = null) { - if (options is null) - { - throw new ArgumentNullException(nameof(options)); - } + options = options ?? ParseOptions.Default; var countryInfo = options.GetCountryInfo(countryCode); if (countryInfo is null) { - throw new ParseException($"The country code {countryCode} is not currently supported, or is not enabled in the ParseOptions."); + throw new ParseException($"The country code {countryCode} is not currently supported, or is not enabled in ParseOptions."); } return Parse(value, countryInfo, options); @@ -175,67 +134,44 @@ public static PhoneNumber Parse(string value, string countryCode, ParseOptions o /// /// Converts the string representation of a phone number to any possible equivalents using the default . A return value indicates whether the conversion succeeded. /// - /// A string containing a phone number. - /// The equivalents if the conversion succeeds, otherwise null. - /// true if value was converted successfully; otherwise, false. - public static bool TryParse(string value, out IEnumerable phoneNumbers) => - TryParse(value, ParseOptions.Default, out phoneNumbers); - - /// - /// Converts the string representation of a phone number to any possible equivalents using the default . A return value indicates whether the conversion succeeded. - /// - /// A string containing a phone number. - /// The options for parsing the phone number. + /// A string containing a phone number in international format (e.g. +XX). /// The equivalents if the conversion succeeds, otherwise null. + /// The to use for parsing the phone number, if not specified (or explicitly set to null) the default parse options are used. /// true if value was converted successfully; otherwise, false. - public static bool TryParse(string value, ParseOptions options, out IEnumerable phoneNumbers) + public static bool TryParse(string value, out IEnumerable phoneNumbers, ParseOptions? options = null) { - if (options is not null) - { - var countries = options.GetCountryInfos(value); + options = options ?? ParseOptions.Default; - phoneNumbers = (countries.Any() ? countries : options.Countries) - .Select(x => options.ParserFactory.GetParser(x).Parse(value)) - .Where(x => x.PhoneNumber is not null) - .Select(x => x.PhoneNumber) - .Cast(); + var countries = options.GetCountryInfos(value); - return true; - } + phoneNumbers = (countries.Any() ? countries : options.Countries) + .Select(x => options.ParserFactory.GetParser(x).Parse(value)) + .Where(x => x.PhoneNumber is not null) + .Select(x => x.PhoneNumber) + .Cast(); - phoneNumbers = Enumerable.Empty(); - return false; + return phoneNumbers.Any(); } /// - /// Converts the string representation of a phone number to its equivalent using the default . A return value indicates whether the conversion succeeded. + /// Converts the string representation of a phone number to its equivalent. A return value indicates whether the conversion succeeded. /// /// A string containing a phone number in international format (e.g. +XX). /// The equivalent if the conversion succeeds, otherwise null. + /// The to use for parsing the phone number, if not specified (or explicitly set to null) the default parse options are used. /// true if value was converted successfully; otherwise, false. - public static bool TryParse(string value, [NotNullWhen(true)] out PhoneNumber? phoneNumber) => - TryParse(value, ParseOptions.Default, out phoneNumber); - - /// - /// Converts the string representation of a phone number to its equivalent using the specified . A return value indicates whether the conversion succeeded. - /// - /// A string containing a phone number in international format (e.g. +XX). - /// The options for parsing the phone number. - /// The equivalent if the conversion succeeds, otherwise null. - /// true if value was converted successfully; otherwise, false. - public static bool TryParse(string value, ParseOptions options, [NotNullWhen(true)] out PhoneNumber? phoneNumber) + public static bool TryParse(string value, [NotNullWhen(true)] out PhoneNumber? phoneNumber, ParseOptions? options = null) { - if (options is not null) + options = options ?? ParseOptions.Default; + + foreach (var countryInfo in options.GetCountryInfos(value)) { - foreach (var countryInfo in options.GetCountryInfos(value)) - { - var result = options.ParserFactory.GetParser(countryInfo).Parse(value); + var result = options.ParserFactory.GetParser(countryInfo).Parse(value); - if (result.PhoneNumber is not null) - { - phoneNumber = result.PhoneNumber; - return true; - } + if (result.PhoneNumber is not null) + { + phoneNumber = result.PhoneNumber; + return true; } } @@ -244,37 +180,18 @@ public static bool TryParse(string value, ParseOptions options, [NotNullWhen(tru } /// - /// Converts the string representation of a phone number to its equivalent using the default . A return value indicates whether the conversion succeeded. - /// - /// A string containing a phone number. - /// The of the country for the phone number. - /// The equivalent if the conversion succeeds, otherwise null. - /// true if value was converted successfully; otherwise, false. - public static bool TryParse(string value, CountryInfo countryInfo, [NotNullWhen(true)] out PhoneNumber? phoneNumber) => - TryParse(value, countryInfo, ParseOptions.Default, out phoneNumber); - - /// - /// Converts the string representation of a phone number to its equivalent using the default . A return value indicates whether the conversion succeeded. + /// Converts the string representation of a phone number to its equivalent. A return value indicates whether the conversion succeeded. /// - /// A string containing a phone number. - /// The ISO 3166 Alpha-2 country code of the country for the phone number. - /// The equivalent if the conversion succeeds, otherwise null. - /// true if value was converted successfully; otherwise, false. - public static bool TryParse(string value, string countryCode, [NotNullWhen(true)] out PhoneNumber? phoneNumber) => - TryParse(value, countryCode, ParseOptions.Default, out phoneNumber); - - /// - /// Converts the string representation of a phone number to its equivalent using the specified . A return value indicates whether the conversion succeeded. - /// - /// A string containing a phone number. + /// A string containing a phone number in the international (e.g. +XX) or national format for the given . /// The of the country for the phone number. - /// The options for parsing phone numbers. /// The equivalent if the conversion succeeds, otherwise null. + /// The to use for parsing the phone number, if not specified (or explicitly set to null) the default parse options are used. /// true if value was converted successfully; otherwise, false. - public static bool TryParse(string value, CountryInfo countryInfo, ParseOptions options, [NotNullWhen(true)] out PhoneNumber? phoneNumber) + public static bool TryParse(string value, CountryInfo countryInfo, [NotNullWhen(true)] out PhoneNumber? phoneNumber, ParseOptions? options = null) { + options = options ?? ParseOptions.Default; + if (countryInfo is not null && - options is not null && options.Countries.Contains(countryInfo)) { var result = options.ParserFactory.GetParser(countryInfo).Parse(value); @@ -288,15 +205,15 @@ options is not null && } /// - /// Converts the string representation of a phone number to its equivalent using the specified . A return value indicates whether the conversion succeeded. + /// Converts the string representation of a phone number to its equivalent. A return value indicates whether the conversion succeeded. /// - /// A string containing a phone number. + /// A string containing a phone number in the international (e.g. +XX) or national format for the given ISO 3166 Alpha-2 country code. /// The ISO 3166 Alpha-2 country code of the country for the phone number. - /// The options for parsing phone numbers. /// The equivalent if the conversion succeeds, otherwise null. + /// The to use for parsing the phone number, if not specified (or explicitly set to null) the default parse options are used. /// true if value was converted successfully; otherwise, false. - public static bool TryParse(string value, string countryCode, ParseOptions options, [NotNullWhen(true)] out PhoneNumber? phoneNumber) => - TryParse(value, options?.GetCountryInfo(countryCode)!, options!, out phoneNumber); + public static bool TryParse(string value, string countryCode, [NotNullWhen(true)] out PhoneNumber? phoneNumber, ParseOptions? options = null) => + TryParse(value, (options ?? ParseOptions.Default).GetCountryInfo(countryCode)!, out phoneNumber, options); /// public override string ToString() => diff --git a/test/PhoneNumbers.Tests/PhoneNumberTests.cs b/test/PhoneNumbers.Tests/PhoneNumberTests.cs index 9acc507c4..7627160bc 100644 --- a/test/PhoneNumbers.Tests/PhoneNumberTests.cs +++ b/test/PhoneNumbers.Tests/PhoneNumberTests.cs @@ -15,6 +15,17 @@ public class PhoneNumberTests public void Parse_Value(string input) => Assert.NotNull(PhoneNumber.Parse(input)); + [Fact] + public void Parse_Value_CallingCode_With_Custom_ParseOptions() + { + var parseOptions = new ParseOptions(); + parseOptions.Countries.Remove(CountryInfo.UnitedKingdom); + + // Should throw as the specified parse options override the default ones and the United Kingdom isn't enabled in the custom parse options + var exception = Assert.Throws(() => PhoneNumber.Parse("01142726444", "GB", parseOptions)); + Assert.Equal("The country code GB is not currently supported, or is not enabled in ParseOptions.", exception.Message); + } + [Theory] [InlineData("0114 272 6444")] [InlineData("0114-272-6444")] @@ -34,13 +45,9 @@ public void Parse_Value_CountryCode(string input) => public void Parse_Value_CountryCode_Throws_If_CountryCode_Not_Supported() { var exception = Assert.Throws(() => PhoneNumber.Parse("0123456789", "ZZ")); - Assert.Equal("The country code ZZ is not currently supported, or is not enabled in the ParseOptions.", exception.Message); + Assert.Equal("The country code ZZ is not currently supported, or is not enabled in ParseOptions.", exception.Message); } - [Fact] - public void Parse_Value_CountryCode_Throws_If_ParseOptions_Null() => - Assert.Throws(() => PhoneNumber.Parse("0123456789", "GB", default)); - [Fact] public void Parse_Value_CountryCode_Throws_If_Value_In_Incorrect_International_Format_For_CountryCode() { @@ -82,10 +89,6 @@ public void Parse_Value_CountryInfo_Throws_If_CountryInfo_Not_Supported() public void Parse_Value_CountryInfo_Throws_If_CountryInfo_Null() => Assert.Throws(() => PhoneNumber.Parse("0123456789", default(CountryInfo))); - [Fact] - public void Parse_Value_CountryInfo_Throws_If_ParseOptions_Null() => - Assert.Throws(() => PhoneNumber.Parse("0123456789", CountryInfo.UnitedKingdom, default)); - [Theory] [InlineData(default(string))] [InlineData("")] @@ -95,8 +98,16 @@ public void Parse_Value_CountryInfo_Throws_If_Value_Invalid(string input) => Assert.Throws(() => PhoneNumber.Parse(input, CountryInfo.UnitedKingdom)); [Fact] - public void Parse_Value_Throws_If_ParseOptions_Null() => - Assert.Throws(() => PhoneNumber.Parse("0123456789", default(ParseOptions))); + public void Parse_Value_CountryInfo_With_Custom_ParseOptions() + { + var parseOptions = new ParseOptions(); + parseOptions.Countries.Remove(CountryInfo.UnitedKingdom); + + // Should throw as the specified parse options override the default ones and the United Kingdom isn't enabled in the custom parse options + var exception = Assert.Throws(() => PhoneNumber.Parse("01142726444", CountryInfo.UnitedKingdom, parseOptions)); + + Assert.Equal("The country United Kingdom is not enabled in ParseOptions.", exception.Message); + } [Theory] [InlineData(default(string))] @@ -106,6 +117,17 @@ public void Parse_Value_Throws_If_ParseOptions_Null() => public void Parse_Value_Throws_If_Value_Invalid(string input) => Assert.Throws(() => PhoneNumber.Parse(input)); + [Fact] + public void Parse_Value_With_Custom_ParseOptions() + { + var parseOptions = new ParseOptions(); + parseOptions.Countries.Remove(CountryInfo.UnitedKingdom); + + // Should throw as the specified parse options override the default ones and the United Kingdom isn't enabled in the custom parse options + var exception = Assert.Throws(() => PhoneNumber.Parse("+441142726444", parseOptions)); + Assert.Equal("The value '+441142726444' could not be successfully parsed into a phone number for any country enabled in ParseOptions.", exception.Message); + } + [Fact] public void ToString_Returns_Default_Formatted_PhoneNumber() { @@ -165,7 +187,10 @@ public void TryParse_Value_CountryCode_False_If_CountryCode_Not_Supported() [Fact] public void TryParse_Value_CountryCode_False_If_ParseOptions_Null() { - Assert.False(PhoneNumber.TryParse("0123456789", "GB", default, out var phoneNumber)); + var parseOptions = new ParseOptions(); + parseOptions.Countries.Remove(CountryInfo.UnitedKingdom); + + Assert.False(PhoneNumber.TryParse("01142726444", "GB", out var phoneNumber, parseOptions)); Assert.Null(phoneNumber); } @@ -223,6 +248,16 @@ public void TryParse_Value_CountryInfo_False_If_Value_Invalid(string input) Assert.Null(phoneNumber); } + [Fact] + public void TryParse_Value_PhoneNumbers_With_Custom_ParseOptions() + { + var parseOptions = new ParseOptions(); + parseOptions.Countries.Clear(); + + Assert.False(PhoneNumber.TryParse("01142726444", out IEnumerable phoneNumbers, parseOptions)); + Assert.Empty(phoneNumbers); + } + [Theory] [InlineData(default(string))] [InlineData("")] @@ -234,17 +269,15 @@ public void TryParse_Value_False_If_Value_Invalid(string input) Assert.Null(phoneNumber); } - [Fact] - public void TryParse_Value_PhoneNumbers_False_If_ParseOptions_Null() - { - Assert.False(PhoneNumber.TryParse("0123456789", default, out IEnumerable phoneNumbers)); - Assert.Empty(phoneNumbers); - } + [Fact] - public void TryParse_Value_To_PhoneNumber_False_If_ParseOptions_Null() + public void TryParse_Value_To_PhoneNumber_With_Custom_ParseOptions() { - Assert.False(PhoneNumber.TryParse("0123456789", default(ParseOptions), out PhoneNumber phoneNumber)); + var parseOptions = new ParseOptions(); + parseOptions.Countries.Remove(CountryInfo.UnitedKingdom); + + Assert.False(PhoneNumber.TryParse("01142726444", out PhoneNumber phoneNumber, parseOptions)); Assert.Null(phoneNumber); }