Skip to content

Commit

Permalink
Refactor inner workings (#50)
Browse files Browse the repository at this point in the history
* update metadata

* revert dependency validator

* refactor

* use record
  • Loading branch information
cedvdb authored Dec 7, 2023
1 parent 5e322a2 commit 7cf63d4
Show file tree
Hide file tree
Showing 19 changed files with 255 additions and 333 deletions.
3 changes: 2 additions & 1 deletion lib/phone_numbers_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
/// More dartdocs go here.
library phone_number_parser;

export 'src/phone_number.dart';

export 'src/models/phone_number_type.dart';
export 'src/models/phone_number.dart';
export 'src/models/phone_number_exceptions.dart';
export 'src/models/iso_code.dart';
export 'src/utils/utils.dart';
Expand Down
12 changes: 7 additions & 5 deletions lib/src/formatters/phone_number_formatter.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import 'dart:math';

import 'package:phone_numbers_parser/src/metadata/metadata_finder.dart';
import 'package:phone_numbers_parser/src/regex/regexp_manager.dart';
import 'package:phone_numbers_parser/src/parsers/_national_number_parser.dart';
import 'package:phone_numbers_parser/src/regex/match_entirely_extension.dart';

import '../metadata/models/phone_metadata_formats.dart';
import '../models/iso_code.dart';
Expand All @@ -14,7 +15,8 @@ class PhoneNumberFormatter {
}
final missingDigits = _getMissingDigits(nsn, isoCode);
final completePhoneNumber = nsn + missingDigits;
final formatingRules = MetadataFinder.getMetadataFormatsForIsoCode(isoCode);
final formatingRules =
MetadataFinder.findMetadataFormatsForIsoCode(isoCode);
final formatingRule = _getMatchingFormatRules(
formatingRules: formatingRules,
nsn: completePhoneNumber,
Expand All @@ -31,7 +33,7 @@ class PhoneNumberFormatter {
transformRule = intlFormat;
}

var formatted = RegexpManager.applyTransformRules(
var formatted = NationalNumberParser.applyTransformRules(
appliedTo: completePhoneNumber,
pattern: formatingRule.pattern,
transformRule: transformRule,
Expand Down Expand Up @@ -62,7 +64,7 @@ class PhoneNumberFormatter {

/// returns 9's to have a valid length number
static String _getMissingDigits(String nsn, IsoCode isoCode) {
final lengthRule = MetadataFinder.getMetadataLengthForIsoCode(isoCode);
final lengthRule = MetadataFinder.findMetadataLengthForIsoCode(isoCode);

final minLength = max(lengthRule.fixedLine.first, lengthRule.mobile.first);
// added digits so we match the pattern in case of an incomplete phone number
Expand Down Expand Up @@ -91,7 +93,7 @@ class PhoneNumberFormatter {
// phonenumberkit seems to be using the last leading digit pattern
// from the list of pattern so that's what we are going to do here as well
final matchLeading = RegExp(rules.leadingDigits.last).matchAsPrefix(nsn);
final matchPattern = RegexpManager.matchEntirely(rules.pattern, nsn);
final matchPattern = rules.pattern.matchEntirely(nsn);
if (matchLeading != null && matchPattern != null) {
return rules;
}
Expand Down
56 changes: 46 additions & 10 deletions lib/src/metadata/metadata_finder.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import '../parsers/_validator.dart';
import 'generated/country_code_to_iso_code.dart';
import 'generated/metadata_by_iso_code.dart';
import 'generated/metadata_formats_by_iso_code.dart';
Expand All @@ -10,9 +11,10 @@ import 'models/phone_metadata_patterns.dart';
import '../models/iso_code.dart';
import '../models/phone_number_exceptions.dart';

/// Helper to find metadata
abstract class MetadataFinder {
/// expects a normalized iso code
static PhoneMetadata getMetadataForIsoCode(IsoCode isoCode) {
static PhoneMetadata findMetadataForIsoCode(IsoCode isoCode) {
final metadata = metadataByIsoCode[isoCode];
if (metadata == null) {
throw PhoneNumberException(
Expand All @@ -24,7 +26,7 @@ abstract class MetadataFinder {
}

/// expects a normalized iso code
static PhoneMetadataPatterns getMetadataPatternsForIsoCode(IsoCode isoCode) {
static PhoneMetadataPatterns findMetadataPatternsForIsoCode(IsoCode isoCode) {
final metadata = metadataPatternsByIsoCode[isoCode];
if (metadata == null) {
throw PhoneNumberException(
Expand All @@ -35,7 +37,7 @@ abstract class MetadataFinder {
return metadata;
}

static PhoneMetadataLengths getMetadataLengthForIsoCode(IsoCode isoCode) {
static PhoneMetadataLengths findMetadataLengthForIsoCode(IsoCode isoCode) {
final metadata = metadataLenghtsByIsoCode[isoCode];
if (metadata == null) {
throw PhoneNumberException(
Expand All @@ -46,7 +48,7 @@ abstract class MetadataFinder {
return metadata;
}

static PhoneMetadataFormats getMetadataFormatsForIsoCode(IsoCode isoCode) {
static PhoneMetadataFormats findMetadataFormatsForIsoCode(IsoCode isoCode) {
final metadata = metadataFormatsByIsoCode[isoCode];
if (metadata == null) {
throw PhoneNumberException(
Expand All @@ -58,19 +60,53 @@ abstract class MetadataFinder {
}

/// expects normalized countryCode
static List<PhoneMetadata> getMetadatasForCountryCode(String countryCode) {
static PhoneMetadata? findMetadataForCountryCode(
String countryCode,
String nationalNumber,
) {
final isoList = _getIsoCodesFromCountryCode(countryCode);
return isoList.map((iso) => getMetadataForIsoCode(iso)).toList();
// country code can have multiple metadata because multiple iso code
// share the same country code.
final allMatchingMetadata =
isoList.map((iso) => findMetadataForIsoCode(iso)).toList();

if (allMatchingMetadata.isEmpty) {
return null;
}
final match = _getMatchUsingPatterns(nationalNumber, allMatchingMetadata);
return match;
}

static List<IsoCode> _getIsoCodesFromCountryCode(String countryCode) {
final isoCodes = countryCodeToIsoCode[countryCode];
if (isoCodes == null) {
throw PhoneNumberException(
code: Code.invalidCountryCallingCode,
description: 'countryCode $countryCode not found',
);
return [];
}
return isoCodes;
}

static PhoneMetadata _getMatchUsingPatterns(
String nationalNumber,
List<PhoneMetadata> potentialFits,
) {
if (potentialFits.length == 1) return potentialFits[0];
// if the phone number is valid for a metadata return that metadata
for (var fit in potentialFits) {
final isValidForIso =
Validator.validateWithPattern(fit.isoCode, nationalNumber);
if (isValidForIso) {
return fit;
}
}
// otherwise the phone number starts with leading digits of metadata
for (var fit in potentialFits) {
final leadingDigits = fit.leadingDigits;
if (leadingDigits != null && nationalNumber.startsWith(leadingDigits)) {
return fit;
}
}

// best guess here
return potentialFits[0];
}
}
45 changes: 0 additions & 45 deletions lib/src/metadata/metadata_matcher.dart

This file was deleted.

70 changes: 18 additions & 52 deletions lib/src/parsers/_country_code_parser.dart
Original file line number Diff line number Diff line change
@@ -1,68 +1,34 @@
import 'dart:math';

import 'package:phone_numbers_parser/phone_numbers_parser.dart';
import 'package:phone_numbers_parser/src/constants/constants.dart';
import 'package:phone_numbers_parser/src/models/phone_number_exceptions.dart';
import 'package:phone_numbers_parser/src/metadata/metadata_finder.dart';

import '_text_parser.dart';

abstract class CountryCodeParser {
/// normalize a country calling code to return only digits
static String normalizeCountryCode(String countryCode) {
countryCode = TextParser.normalize(countryCode);

if (countryCode.startsWith('+')) {
countryCode = countryCode.replaceFirst('+', '');
}
// country code don't start with zero
if (countryCode.startsWith('0')) {
throw PhoneNumberException(
code: Code.invalidCountryCallingCode,
description:
'country calling code do not start with 0, was $countryCode');
}
if (int.tryParse(countryCode) == null) {
throw PhoneNumberException(
code: Code.invalidCountryCallingCode,
description: 'country calling code must be digits, was $countryCode. '
'Maybe you wanted to parseWithIsoCode ?');
}
if (countryCode.length < Constants.minLengthCountryCallingCode ||
countryCode.length > Constants.maxLengthCountryCallingCode) {
throw PhoneNumberException(
code: Code.invalidCountryCallingCode,
description:
'country calling code has an invalid length, was $countryCode');
}
return countryCode;
}

/// tries to find a country calling code at the start of a phone number
static String extractCountryCode(String phoneNumber) {
final maxLength =
min(phoneNumber.length, Constants.maxLengthCountryCallingCode);
var potentialCountryCode = phoneNumber.substring(0, maxLength);
potentialCountryCode = normalizeCountryCode(potentialCountryCode);
static (String countryCode, String nsn) extractCountryCode(
String phoneNumber,
) {
final maxCountryCodeLength = min(
phoneNumber.length,
Constants.maxLengthCountryCallingCode,
);
var potentialCountryCode = phoneNumber.substring(0, maxCountryCodeLength);

for (var i = 1; i <= potentialCountryCode.length; i++) {
try {
final potentialCountryCodeFit = potentialCountryCode.substring(0, i);
MetadataFinder.getMetadatasForCountryCode(potentialCountryCodeFit);
return potentialCountryCodeFit;
// ignore: empty_catches
} catch (e) {}
final potentialCountryCodeFit = potentialCountryCode.substring(0, i);
final nsn = phoneNumber.substring(i);
final countryMetadata = MetadataFinder.findMetadataForCountryCode(
potentialCountryCodeFit,
nsn,
);
if (countryMetadata != null) {
return (countryMetadata.countryCode, nsn);
}
}
throw PhoneNumberException(
code: Code.notFound,
description:
'country calling code not found in phone number $phoneNumber');
}

// removes the country code at the start of a phone number
static String removeCountryCode(String phoneNumber, String countryCode) {
if (phoneNumber.startsWith(countryCode)) {
return phoneNumber.substring(countryCode.length);
}
return phoneNumber;
}
}
20 changes: 12 additions & 8 deletions lib/src/parsers/_international_prefix_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ abstract class InternationalPrefixParser {
/// if starts with 00 or 011
/// we consider those as internationalPrefix as
/// they cover 4/5 of the international prefix
static String removeExitCode(
static (String exitCode, String phoneNumberWithoutExitCode) extractExitCode(
String phoneNumber, {
PhoneMetadata? callerCountryMetadata,
PhoneMetadata? destinationCountryMetadata,
}) {
if (phoneNumber.startsWith('+')) {
return phoneNumber.substring(1);
return ('+', phoneNumber.substring(1));
}

// if the caller country was provided it's easy, just remove the exit code
Expand All @@ -30,27 +30,31 @@ abstract class InternationalPrefixParser {
// then no such check is made that a country code does not follow
final countryCode = destinationCountryMetadata?.countryCode ?? '';
if (phoneNumber.startsWith('00$countryCode')) {
return phoneNumber.substring(2);
return ('00', phoneNumber.substring(2));
}

if (phoneNumber.startsWith('011$countryCode')) {
return phoneNumber.substring(3);
return ('011', phoneNumber.substring(3));
}

return phoneNumber;
return ('', phoneNumber);
}

static String _removeExitCodeWithMetadata(
static (String exitCode, String phoneNumberWithoutExitCode)
_removeExitCodeWithMetadata(
String phoneNumber,
PhoneMetadata metadata,
) {
final match =
RegExp(metadata.internationalPrefix).matchAsPrefix(phoneNumber);
if (match != null) {
return phoneNumber.substring(match.end);
return (
phoneNumber.substring(match.start, match.end),
phoneNumber.substring(match.end)
);
}
// if it does not start with the international prefix from the
// country we assume the prefix is not present
return phoneNumber;
return ('', phoneNumber);
}
}
Loading

0 comments on commit 7cf63d4

Please sign in to comment.