git.iko.soy/typeable/libphonenumber
on ee957b0 help sign in
13 files changed+183−663
since splitsnapshots
unifiedsplit
.github/workflows/ci.yaml−102 LICENSE−21 cxxbits/c_phonenumber.cpp+4−6 cxxbits/c_phonenumberutil.cpp+26−45 include/c_phonenumber.h+8−4 include/c_phonenumberutil.h+20−20 include/common.h+2−3 libphonenumber.cabal+36−81 src/Data/PhoneNumber/Internal/Number.chs+1−1 src/Data/PhoneNumber/Number.hs+29−57 src/Data/PhoneNumber/Util.hs+51−72 test/Leak.hs+6−6 test/Spec.hs−245
.github/workflows/ci.yamldeleted−102
@@ -1,102 +0,0 @@
1−name: CI
2−
3−on:
4− push:
5− branches:
6− - "*"
7− pull_request:
8− branches:
9− - "*"
10− release:
11− types:
12− - prereleased
13−
14−jobs:
15− build:
16− strategy:
17− matrix:
18− platform:
19− - ubuntu-latest
20− ghc_version:
21− - "8.6.5"
22− - "8.8.4"
23− - "8.10.7"
24− - "9.0.2"
25− - "9.2.8"
26− - "9.4.8"
27− - "9.6.7"
28− - "9.8.4"
29− - "9.10.3"
30− - "9.12.2"
31− - "9.14.1"
32− fail-fast: false
33− name: Build on Linux, GHC ${{ matrix.ghc_version }}
34− runs-on: ${{ matrix.platform }}
35− steps:
36− - uses: actions/checkout@v4
37− - uses: haskell-actions/setup@v2
38− with:
39− ghc-version: ${{ matrix.ghc_version }}
40− cabal-version: "3.10.1.0"
41− - name: Install C++ library
42− run: |
43− sudo apt-get update
44− sudo apt-get install -y libphonenumber-dev
45−
46− - name: cabal sdist
47− run: cabal sdist --output-dir "$GITHUB_WORKSPACE/sdist"
48− - name: unpack
49− run: |
50− mkdir -p "$GITHUB_WORKSPACE/unpacked"
51− find "$GITHUB_WORKSPACE/sdist" -maxdepth 1 -type f -name '*.tar.gz' -exec tar -C "$GITHUB_WORKSPACE/unpacked" -xzvf {} \;
52− echo "PKGDIR_libphonenumber=$(find "$GITHUB_WORKSPACE/unpacked" -maxdepth 1 -type d -regex '.*/libphonenumber-[0-9.]*')" >> "$GITHUB_ENV"
53− - name: cabal configure
54− run: cd "$PKGDIR_libphonenumber" && cabal configure --with-ghc=ghc-${{ matrix.ghc_version }}
55− - name: Install dependencies
56− run: cd "$PKGDIR_libphonenumber" && cabal build --dependencies-only --enable-tests --haddock-all
57− - name: cabal build
58− run: cd "$PKGDIR_libphonenumber" && cabal build --ghc-options=-Werror
59− - name: cabal test spec_test
60− run: cd "$PKGDIR_libphonenumber" && cabal test spec_test
61− - name: cabal check
62− run: cd "$PKGDIR_libphonenumber" && cabal check
63− - name: cabal haddock
64− run: cd "$PKGDIR_libphonenumber" && cabal haddock
65−
66− dist:
67− needs: build
68− runs-on: ubuntu-latest
69− name: Build source tarball
70− if: github.event_name == 'release'
71− steps:
72− - uses: actions/checkout@v3
73− - uses: haskell-actions/setup@v2
74− with:
75− cabal-version: "3.10.1.0"
76− - name: Install C++ library
77− run: |
78− sudo apt-get update
79− sudo apt-get install -y libphonenumber-dev
80− - name: cabal sdist
81− run: |
82− cabal sdist --output-dir "$GITHUB_WORKSPACE/sdist"
83− echo "dist=$(find "$GITHUB_WORKSPACE/sdist" -maxdepth 1 -type f -name '*.tar.gz')" >> "$GITHUB_ENV"
84− - name: cabal haddock
85− run: |
86− cabal haddock --builddir "$GITHUB_WORKSPACE/doc" --haddock-for-hackage --enable-doc
87− echo "docs=$(find "$GITHUB_WORKSPACE/doc" -maxdepth 1 -type f -name '*-docs.tar.gz')" >> "$GITHUB_ENV"
88− - uses: actions/upload-artifact@v6
89− with:
90− name: Source tarball
91− path: ${{ env.dist }}
92− - uses: svenstaro/upload-release-action@2.11.3
93− with:
94− repo_token: ${{ secrets.GITHUB_TOKEN }}
95− file: ${{ env.dist }}
96− tag: ${{ github.ref }}
97− - name: Upload tarball to hackage
98− run: |
99− curl -X POST 'https://hackage.haskell.org/packages/candidates/' -H "Authorization: X-ApiKey ${{ secrets.HACKAGE_KEY }}" -F "package=@$dist" -w '%{url_effective}\n'
100− - name: Upload haddocks to hackage
101− run: |
102− curl -X PUT "https://hackage.haskell.org/package/$(basename ${docs%-docs.tar.gz})/candidate/docs" -H "Authorization: X-ApiKey ${{ secrets.HACKAGE_KEY }}" -H 'Content-type: application/x-tar' -H 'Content-encoding: gzip' --data-binary "@$docs"
LICENSEdeleted−21
@@ -1,21 +0,0 @@
1−MIT License
2−
3−Copyright (c) 2023 Typeable
4−
5−Permission is hereby granted, free of charge, to any person obtaining a copy
6−of this software and associated documentation files (the "Software"), to deal
7−in the Software without restriction, including without limitation the rights
8−to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9−copies of the Software, and to permit persons to whom the Software is
10−furnished to do so, subject to the following conditions:
11−
12−The above copyright notice and this permission notice shall be included in all
13−copies or substantial portions of the Software.
14−
15−THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16−IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17−FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18−AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19−LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20−OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21−SOFTWARE.
cxxbits/c_phonenumber.cpp+4−6
@@ -55,7 +55,7 @@ static country_code_source unmarshal_country_code_source(PhoneNumber::CountryCod
5555 return static_cast<country_code_source>(value);
5656}
5757
58−extern "C" CxxPhoneNumber *c_phone_number_marshal(phone_number const *c_number)
58+extern "C" PhoneNumber *c_phone_number_marshal(phone_number const *c_number)
5959{
6060 PhoneNumber *number = new PhoneNumber();
6161 if (c_number->extension != nullptr)
@@ -72,12 +72,11 @@ extern "C" CxxPhoneNumber *c_phone_number_marshal(phone_number const *c_number)
7272 number->set_country_code_source(marshal_country_code_source(c_number->country_code_source));
7373 if (c_number->has_number_of_leading_zeros)
7474 number->set_number_of_leading_zeros(c_number->number_of_leading_zeros);
75− return reinterpret_cast<CxxPhoneNumber *>(number);
75+ return number;
7676}
7777
78−extern "C" void c_phone_number_unmarshal(CxxPhoneNumber const *ptr, phone_number *c_number)
78+extern "C" void c_phone_number_unmarshal(PhoneNumber const *number, phone_number *c_number)
7979{
80− auto number = reinterpret_cast<PhoneNumber const *>(ptr);
8180 if (number->has_extension())
8281 unmarshal_string_size(number->extension(), c_number->extension, c_number->extension_size);
8382 else
@@ -100,8 +99,7 @@ extern "C" void c_phone_number_unmarshal(CxxPhoneNumber const *ptr, phone_number
10099 c_number->number_of_leading_zeros = number->number_of_leading_zeros();
101100}
102101
103−extern "C" void c_phone_number_free(CxxPhoneNumber *ptr)
102+extern "C" void c_phone_number_free(PhoneNumber *number)
104103{
105− auto number = reinterpret_cast<PhoneNumber *>(ptr);
106104 delete number;
107105}
cxxbits/c_phonenumberutil.cpp+26−45
@@ -310,11 +310,10 @@ extern "C" void c_phone_number_util_normalize_dialable_chars_only(
310310}
311311
312312extern "C" void c_phone_number_util_get_national_significant_number(
313− CxxPhoneNumber const *ptr,
313+ PhoneNumber const *number,
314314 c_string *c_nsn
315315)
316316{
317− auto number = reinterpret_cast<PhoneNumber const *>(ptr);
318317 std::string nsn;
319318 PhoneNumberUtil::GetInstance()->GetNationalSignificantNumber(*number, &nsn);
320319 unmarshal_c_string(nsn, *c_nsn);
@@ -331,25 +330,23 @@ extern "C" void c_phone_number_util_get_country_mobile_token(
331330}
332331
333332extern "C" void c_phone_number_util_format(
334− CxxPhoneNumber const *ptr,
333+ PhoneNumber const *number,
335334 phone_number_format format,
336335 c_string *c_string
337336)
338337{
339− auto number = reinterpret_cast<PhoneNumber const *>(ptr);
340338 std::string string;
341339 PhoneNumberUtil::GetInstance()->Format(*number, marshal_phone_number_format(format), &string);
342340 unmarshal_c_string(string, *c_string);
343341}
344342
345343extern "C" void c_phone_number_util_format_national_number_with_carrier_code(
346− CxxPhoneNumber const *ptr,
344+ PhoneNumber const *number,
347345 char const *c_carrier_code,
348346 size_t c_carrier_code_size,
349347 c_string *c_string
350348)
351349{
352− auto number = reinterpret_cast<PhoneNumber const *>(ptr);
353350 std::string const carrier_code(c_carrier_code, c_carrier_code_size);
354351 std::string string;
355352 PhoneNumberUtil::GetInstance()->FormatNationalNumberWithCarrierCode(*number, carrier_code, &string);
@@ -357,13 +354,12 @@ extern "C" void c_phone_number_util_format_national_number_with_carrier_code(
357354}
358355
359356extern "C" void c_phone_number_util_format_national_number_with_preferred_carrier_code(
360− CxxPhoneNumber const *ptr,
357+ PhoneNumber const *number,
361358 char const *c_carrier_code,
362359 size_t c_carrier_code_size,
363360 c_string *c_string
364361)
365362{
366− auto number = reinterpret_cast<PhoneNumber const *>(ptr);
367363 std::string const carrier_code(c_carrier_code, c_carrier_code_size);
368364 std::string string;
369365 PhoneNumberUtil::GetInstance()->FormatNationalNumberWithPreferredCarrierCode(*number, carrier_code, &string);
@@ -371,14 +367,13 @@ extern "C" void c_phone_number_util_format_national_number_with_preferred_carrie
371367}
372368
373369extern "C" void c_phone_number_util_format_number_for_mobile_dialing(
374− CxxPhoneNumber const *ptr,
370+ PhoneNumber const *number,
375371 char const *c_region,
376372 size_t c_region_size,
377373 int with_formatting,
378374 c_string *c_string
379375)
380376{
381− auto number = reinterpret_cast<PhoneNumber const *>(ptr);
382377 std::string const region(c_region, c_region_size);
383378 std::string string;
384379 PhoneNumberUtil::GetInstance()->FormatNumberForMobileDialing(*number, region, with_formatting, &string);
@@ -386,13 +381,12 @@ extern "C" void c_phone_number_util_format_number_for_mobile_dialing(
386381}
387382
388383extern "C" void c_phone_number_util_format_out_of_country_calling_number(
389− CxxPhoneNumber const *ptr,
384+ PhoneNumber const *number,
390385 char const *c_region,
391386 size_t c_region_size,
392387 c_string *c_string
393388)
394389{
395− auto number = reinterpret_cast<PhoneNumber const *>(ptr);
396390 std::string const region(c_region, c_region_size);
397391 std::string string;
398392 PhoneNumberUtil::GetInstance()->FormatOutOfCountryCallingNumber(*number, region, &string);
@@ -400,13 +394,12 @@ extern "C" void c_phone_number_util_format_out_of_country_calling_number(
400394}
401395
402396extern "C" void c_phone_number_util_format_in_original_format(
403− CxxPhoneNumber const *ptr,
397+ PhoneNumber const *number,
404398 char const *c_region,
405399 size_t c_region_size,
406400 c_string *c_string
407401)
408402{
409− auto number = reinterpret_cast<PhoneNumber const *>(ptr);
410403 std::string const region(c_region, c_region_size);
411404 std::string string;
412405 PhoneNumberUtil::GetInstance()->FormatInOriginalFormat(*number, region, &string);
@@ -414,26 +407,24 @@ extern "C" void c_phone_number_util_format_in_original_format(
414407}
415408
416409extern "C" void c_phone_number_util_format_out_of_country_keeping_alpha_chars(
417− CxxPhoneNumber const *ptr,
410+ PhoneNumber const *number,
418411 char const *c_region,
419412 size_t c_region_size,
420413 c_string *c_string
421414)
422415{
423− auto number = reinterpret_cast<PhoneNumber const *>(ptr);
424416 std::string const region(c_region, c_region_size);
425417 std::string string;
426418 PhoneNumberUtil::GetInstance()->FormatOutOfCountryKeepingAlphaChars(*number, region, &string);
427419 unmarshal_c_string(string, *c_string);
428420}
429421
430−extern "C" CxxPhoneNumber *c_phone_number_util_truncate_too_long_number(CxxPhoneNumber const *ptr)
422+extern "C" PhoneNumber *c_phone_number_util_truncate_too_long_number(PhoneNumber const *input)
431423{
432− auto input = reinterpret_cast<PhoneNumber const *>(ptr);
433424 auto *output = new PhoneNumber(*input);
434425 if (PhoneNumberUtil::GetInstance()->TruncateTooLongNumber(output))
435426 {
436− return reinterpret_cast<CxxPhoneNumber *>(output);
427+ return output;
437428 }
438429 else
439430 {
@@ -442,37 +433,33 @@ extern "C" CxxPhoneNumber *c_phone_number_util_truncate_too_long_number(CxxPhone
442433 }
443434}
444435
445−extern "C" phone_number_type c_phone_number_util_get_number_type(CxxPhoneNumber const *ptr)
436+extern "C" phone_number_type c_phone_number_util_get_number_type(PhoneNumber const *number)
446437{
447− auto number = reinterpret_cast<PhoneNumber const *>(ptr);
448438 return unmarshal_phone_number_type(
449439 PhoneNumberUtil::GetInstance()->GetNumberType(*number)
450440 );
451441}
452442
453−extern "C" int c_phone_number_util_is_valid_number(CxxPhoneNumber const *ptr)
443+extern "C" int c_phone_number_util_is_valid_number(PhoneNumber const *number)
454444{
455− auto number = reinterpret_cast<PhoneNumber const *>(ptr);
456445 return PhoneNumberUtil::GetInstance()->IsValidNumber(*number);
457446}
458447
459448extern "C" int c_phone_number_util_is_valid_number_for_region(
460− CxxPhoneNumber const *ptr,
449+ PhoneNumber const *number,
461450 char const *c_region,
462451 size_t c_region_size
463452)
464453{
465− auto number = reinterpret_cast<PhoneNumber const *>(ptr);
466454 std::string const region(c_region, c_region_size);
467455 return PhoneNumberUtil::GetInstance()->IsValidNumberForRegion(*number, region);
468456}
469457
470458extern "C" void c_phone_number_util_get_region_code_for_number(
471− CxxPhoneNumber const *ptr,
459+ PhoneNumber const *number,
472460 c_string *c_region
473461)
474462{
475− auto number = reinterpret_cast<PhoneNumber const *>(ptr);
476463 std::string region;
477464 PhoneNumberUtil::GetInstance()->GetRegionCodeForNumber(*number, &region);
478465 unmarshal_c_string(region, *c_region);
@@ -531,11 +518,10 @@ extern "C" void c_phone_number_util_get_ndd_prefix_for_region(
531518}
532519
533520extern "C" validation_result c_phone_number_util_is_possible_number_for_type_with_reason(
534− CxxPhoneNumber const *ptr,
521+ PhoneNumber const *number,
535522 phone_number_type ntype
536523)
537524{
538− auto number = reinterpret_cast<PhoneNumber const *>(ptr);
539525 return unmarshal_validation_result(
540526 PhoneNumberUtil::GetInstance()->IsPossibleNumberForTypeWithReason(
541527 *number,
@@ -544,15 +530,13 @@ extern "C" validation_result c_phone_number_util_is_possible_number_for_type_wit
544530 );
545531}
546532
547−extern "C" int c_phone_number_util_can_be_internationally_dialed(CxxPhoneNumber const *ptr)
533+extern "C" int c_phone_number_util_can_be_internationally_dialed(PhoneNumber const *number)
548534{
549− auto number = reinterpret_cast<PhoneNumber const *>(ptr);
550535 return PhoneNumberUtil::GetInstance()->CanBeInternationallyDialled(*number);
551536}
552537
553−extern "C" int c_phone_number_util_is_number_geographical_1(CxxPhoneNumber const *ptr)
538+extern "C" int c_phone_number_util_is_number_geographical_1(PhoneNumber const *number)
554539{
555− auto number = reinterpret_cast<PhoneNumber const *>(ptr);
556540 return PhoneNumberUtil::GetInstance()->IsNumberGeographical(*number);
557541}
558542
@@ -572,14 +556,14 @@ extern "C" error_type c_phone_number_util_parse(
572556 size_t c_string_size,
573557 char const *c_region,
574558 size_t c_region_size,
575− CxxPhoneNumber **number
559+ PhoneNumber **number
576560)
577561{
578562 std::string const string(c_string, c_string_size);
579563 std::string const region(c_region, c_region_size);
580− *number = reinterpret_cast<CxxPhoneNumber *>(new PhoneNumber());
564+ *number = new PhoneNumber();
581565 return unmarshal_error_type(
582− PhoneNumberUtil::GetInstance()->Parse(string, region, reinterpret_cast<PhoneNumber *>(*number))
566+ PhoneNumberUtil::GetInstance()->Parse(string, region, *number)
583567 );
584568}
585569
@@ -588,24 +572,22 @@ extern "C" error_type c_phone_number_util_parse_and_keep_raw_input(
588572 size_t c_string_size,
589573 char const *c_region,
590574 size_t c_region_size,
591− CxxPhoneNumber **number
575+ PhoneNumber **number
592576)
593577{
594578 std::string const string(c_string, c_string_size);
595579 std::string const region(c_region, c_region_size);
596− *number = reinterpret_cast<CxxPhoneNumber *>(new PhoneNumber());
580+ *number = new PhoneNumber();
597581 return unmarshal_error_type(
598− PhoneNumberUtil::GetInstance()->ParseAndKeepRawInput(string, region, reinterpret_cast<PhoneNumber *>(*number))
582+ PhoneNumberUtil::GetInstance()->ParseAndKeepRawInput(string, region, *number)
599583 );
600584}
601585
602586extern "C" match_type c_phone_number_util_is_number_match(
603− CxxPhoneNumber const *ptr1,
604− CxxPhoneNumber const *ptr2
587+ PhoneNumber const *number1,
588+ PhoneNumber const *number2
605589)
606590{
607− auto number1 = reinterpret_cast<PhoneNumber const *>(ptr1);
608− auto number2 = reinterpret_cast<PhoneNumber const *>(ptr2);
609591 return unmarshal_match_type(
610592 PhoneNumberUtil::GetInstance()->IsNumberMatch(*number1, *number2)
611593 );
@@ -626,12 +608,11 @@ extern "C" match_type c_phone_number_util_is_number_match_with_two_strings(
626608}
627609
628610extern "C" match_type c_phone_number_util_is_number_match_with_one_string(
629− CxxPhoneNumber const *ptr1,
611+ PhoneNumber const *number1,
630612 char const *c_number2,
631613 size_t c_number2_size
632614)
633615{
634− auto number1 = reinterpret_cast<PhoneNumber const *>(ptr1);
635616 std::string const number2(c_number2, c_number2_size);
636617 return unmarshal_match_type(
637618 PhoneNumberUtil::GetInstance()->IsNumberMatchWithOneString(*number1, number2)
include/c_phonenumber.h+8−4
@@ -43,16 +43,20 @@ typedef struct
4343 char has_number_of_leading_zeros;
4444} phone_number;
4545
46−struct CxxPhoneNumber;
46+#ifdef __cplusplus
47+typedef i18n::phonenumbers::PhoneNumber PhoneNumber;
48+#else
49+typedef struct PhoneNumber PhoneNumber;
50+#endif
4751
4852// The returned object is allocated by `new` and managed by C++ RAII, and it is
4953// haskell's responsibility to call its finalizer (c_phone_number_free).
50−struct CxxPhoneNumber *c_phone_number_marshal(phone_number const *);
54+PhoneNumber *c_phone_number_marshal(phone_number const *);
5155
5256// phone_number * points to an area which will be filled with the data of the
5357// given PhoneNumber.
54−void c_phone_number_unmarshal(struct CxxPhoneNumber const *, phone_number *);
58+void c_phone_number_unmarshal(PhoneNumber const *, phone_number *);
5559
56−void c_phone_number_free(struct CxxPhoneNumber *);
60+void c_phone_number_free(PhoneNumber *);
5761
5862#endif
include/c_phonenumberutil.h+20−20
@@ -97,34 +97,34 @@ int c_phone_number_util_is_alpha_number(char const *, size_t);
9797void c_phone_number_util_convert_alpha_characters_in_number(char const *, size_t, c_string *);
9898void c_phone_number_util_normalize_digits_only(char const *, size_t, c_string *);
9999void c_phone_number_util_normalize_dialable_chars_only(char const *, size_t, c_string *);
100−void c_phone_number_util_get_national_significant_number(struct CxxPhoneNumber const *, c_string *);
100+void c_phone_number_util_get_national_significant_number(PhoneNumber const *, c_string *);
101101void c_phone_number_util_get_country_mobile_token(int, c_string *);
102−void c_phone_number_util_format(struct CxxPhoneNumber const *, enum phone_number_format, c_string *);
103−void c_phone_number_util_format_national_number_with_carrier_code(struct CxxPhoneNumber const *, char const *, size_t, c_string *);
104−void c_phone_number_util_format_national_number_with_preferred_carrier_code(struct CxxPhoneNumber const *, char const *, size_t, c_string *);
105−void c_phone_number_util_format_number_for_mobile_dialing(struct CxxPhoneNumber const *, char const *, size_t, int, c_string *);
106−void c_phone_number_util_format_out_of_country_calling_number(struct CxxPhoneNumber const *, char const *, size_t, c_string *);
107−void c_phone_number_util_format_in_original_format(struct CxxPhoneNumber const *, char const *, size_t, c_string *);
108−void c_phone_number_util_format_out_of_country_keeping_alpha_chars(struct CxxPhoneNumber const *, char const *, size_t, c_string *);
102+void c_phone_number_util_format(PhoneNumber const *, enum phone_number_format, c_string *);
103+void c_phone_number_util_format_national_number_with_carrier_code(PhoneNumber const *, char const *, size_t, c_string *);
104+void c_phone_number_util_format_national_number_with_preferred_carrier_code(PhoneNumber const *, char const *, size_t, c_string *);
105+void c_phone_number_util_format_number_for_mobile_dialing(PhoneNumber const *, char const *, size_t, int, c_string *);
106+void c_phone_number_util_format_out_of_country_calling_number(PhoneNumber const *, char const *, size_t, c_string *);
107+void c_phone_number_util_format_in_original_format(PhoneNumber const *, char const *, size_t, c_string *);
108+void c_phone_number_util_format_out_of_country_keeping_alpha_chars(PhoneNumber const *, char const *, size_t, c_string *);
109109// /!\ May return NULL
110−struct CxxPhoneNumber *c_phone_number_util_truncate_too_long_number(struct CxxPhoneNumber const *);
111−enum phone_number_type c_phone_number_util_get_number_type(struct CxxPhoneNumber const *);
112−int c_phone_number_util_is_valid_number(struct CxxPhoneNumber const *);
113−int c_phone_number_util_is_valid_number_for_region(struct CxxPhoneNumber const *, char const *, size_t);
114−void c_phone_number_util_get_region_code_for_number(struct CxxPhoneNumber const *, c_string *);
110+PhoneNumber *c_phone_number_util_truncate_too_long_number(PhoneNumber const *);
111+enum phone_number_type c_phone_number_util_get_number_type(PhoneNumber const *);
112+int c_phone_number_util_is_valid_number(PhoneNumber const *);
113+int c_phone_number_util_is_valid_number_for_region(PhoneNumber const *, char const *, size_t);
114+void c_phone_number_util_get_region_code_for_number(PhoneNumber const *, c_string *);
115115int c_phone_number_util_get_country_code_for_region(char const *, size_t);
116116void c_phone_number_util_get_region_code_for_country_code(int, c_string *);
117117void c_phone_number_util_get_region_codes_for_country_calling_code(int, c_string **, size_t *);
118118int c_phone_number_util_is_nanpa_country(char const *, size_t);
119119void c_phone_number_util_get_ndd_prefix_for_region(char const *, size_t, int, c_string *);
120−enum validation_result c_phone_number_util_is_possible_number_for_type_with_reason(struct CxxPhoneNumber const *, enum phone_number_type);
121−int c_phone_number_util_can_be_internationally_dialed(struct CxxPhoneNumber const *);
122−int c_phone_number_util_is_number_geographical_1(struct CxxPhoneNumber const *);
120+enum validation_result c_phone_number_util_is_possible_number_for_type_with_reason(PhoneNumber const *, enum phone_number_type);
121+int c_phone_number_util_can_be_internationally_dialed(PhoneNumber const *);
122+int c_phone_number_util_is_number_geographical_1(PhoneNumber const *);
123123int c_phone_number_util_is_number_geographical_2(enum phone_number_type, int);
124−enum error_type c_phone_number_util_parse(char const *, size_t, char const *, size_t, struct CxxPhoneNumber **);
125−enum error_type c_phone_number_util_parse_and_keep_raw_input(char const *, size_t, char const *, size_t, struct CxxPhoneNumber **);
126−enum match_type c_phone_number_util_is_number_match(struct CxxPhoneNumber const *, struct CxxPhoneNumber const *);
124+enum error_type c_phone_number_util_parse(char const *, size_t, char const *, size_t, PhoneNumber **);
125+enum error_type c_phone_number_util_parse_and_keep_raw_input(char const *, size_t, char const *, size_t, PhoneNumber **);
126+enum match_type c_phone_number_util_is_number_match(PhoneNumber const *, PhoneNumber const *);
127127enum match_type c_phone_number_util_is_number_match_with_two_strings(char const *, size_t, char const *, size_t);
128−enum match_type c_phone_number_util_is_number_match_with_one_string(struct CxxPhoneNumber const *, char const *, size_t);
128+enum match_type c_phone_number_util_is_number_match_with_one_string(PhoneNumber const *, char const *, size_t);
129129
130130#endif
include/common.h+2−3
@@ -4,7 +4,6 @@
44#include <cstddef>
55#include <cstring>
66#include <type_traits>
7−#include <utility>
87
98// Given `enum T { A, B, C }` and `enum S { X, Y, Z }`, we can use
109// `is_same_enum<enum_values<T, A, B, C>, enum_values<S, X, Y, Z>>::value`
@@ -32,7 +31,7 @@ struct is_same_enum: std::false_type
3231
3332template<typename T, T... TVals, typename S, S... SVals>
3433struct is_same_enum<enum_values<T, TVals...>, enum_values<S, SVals...>>:
35− std::integral_constant<bool, std::is_same<
34+ std::integral_constant<bool, std::is_same_v<
3635 std::integer_sequence<
3736 typename std::common_type<
3837 typename std::underlying_type<T>::type,
@@ -47,7 +46,7 @@ struct is_same_enum<enum_values<T, TVals...>, enum_values<S, SVals...>>:
4746 >::type,
4847 SVals...
4948 >
50− >::value>
49+ >>
5150{};
5251
5352inline void unmarshal_string_size(std::string const &string, char *&c_string, size_t &c_string_size)
libphonenumber.cabal+36−81
@@ -1,6 +1,6 @@
11cabal-version: 3.0
22name: libphonenumber
3−version: 0.1.4.0
3+version: 0.0.0.0
44category: Data
55synopsis: Parsing, formatting, and validating international phone
66 numbers
@@ -8,8 +8,7 @@ description:
88 This package provides bindings for the C++ version of the
99 <https://github.com/google/libphonenumber libphonenumber library>.
1010 The bindings currently do not include @AsYouTypeFormatter@
11− and @ShortNumberInfo@. The C++ library must be of version
12− >= 8.7.0.
11+ and @ShortNumberInfo@.
1312
1413 The interface largely resembles that of the original unified
1514 C++, Java, and JavaScript version of the library, with some
@@ -20,66 +19,37 @@ description:
2019 else has installed a global logger which could observe
2120 side-effects from library calls.
2221
23− The underlying library internally uses UTF-8 encoded byte
24− strings. To avoid decoding overhead where it is unnecessary,
25− and to avoid a dependency on @text@, we use 'ByteString'
26− throughout the library instead of 'Data.Text.Text'.
27−
28−copyright: 2023-2025 Typeable
29−author: Typeable
30−maintainer: mniip@typeable.io
31−homepage: https://github.com/typeable/libphonenumber/
32−license: MIT
33−license-file: LICENSE
34−
35−tested-with:
36− , GHC == 8.6.5
37− , GHC == 8.8.4
38− , GHC == 8.10.7
39− , GHC == 9.0.2
40− , GHC == 9.2.8
41− , GHC == 9.4.8
42− , GHC == 9.6.7
43− , GHC == 9.8.4
44− , GHC == 9.10.3
45− , GHC == 9.12.2
46− , GHC == 9.14.1
47−
48−build-type: Simple
22+build-type: Simple
4923
5024extra-source-files:
51− , include/*.h
52− , test/libphonenumber.supp
53−
54−source-repository head
55− type: git
56− location: https://github.com/typeable/libphonenumber/
25+ include/*.h
26+ test/libphonenumber.supp
5727
5828library
5929 exposed-modules:
60− , Data.PhoneNumber.Number
61− , Data.PhoneNumber.Util
30+ Data.PhoneNumber.Number
31+ Data.PhoneNumber.Util
6232 other-modules:
63− , Data.PhoneNumber.Internal.Common
64− , Data.PhoneNumber.Internal.Number
65− , Data.PhoneNumber.Internal.Util
33+ Data.PhoneNumber.Internal.Common
34+ Data.PhoneNumber.Internal.Number
35+ Data.PhoneNumber.Internal.Util
6636 build-depends:
67− , base >= 4.12 && < 4.23
68− , bytestring >= 0.10.8 && < 0.13
69− , containers >= 0.6.0 && < 0.9
70− , deepseq >= 1.4.4 && < 1.6
71− , transformers >= 0.5.5 && < 0.7
37+ base >= 4.12 && < 4.18
38+ , bytestring
39+ , containers
40+ , deepseq
41+ , transformers
7242 build-tool-depends: c2hs:c2hs
73− pkgconfig-depends: protobuf
7443 include-dirs: include
7544 includes:
76− , c_phonenumber.h
77− , c_phonenumberutil.h
45+ c_phonenumber.h
46+ c_phonenumberutil.h
7847 cxx-sources:
79− , cxxbits/c_phonenumber.cpp
80− , cxxbits/c_phonenumberutil.cpp
48+ cxxbits/c_phonenumber.cpp
49+ cxxbits/c_phonenumberutil.cpp
8150 extra-libraries:
82− , phonenumber
51+ phonenumber
52+ protobuf
8353 if impl(ghc >= 9.4)
8454 build-depends: system-cxx-std-lib
8555 else
@@ -87,43 +57,28 @@ library
8757 hs-source-dirs: src
8858 default-language: Haskell2010
8959 default-extensions:
90− , DeriveAnyClass
91− , DeriveDataTypeable
92− , DeriveGeneric
93− , DerivingStrategies
94− , GeneralizedNewtypeDeriving
95− , LambdaCase
96− , NamedFieldPuns
97− , OverloadedStrings
98− , PatternSynonyms
99− , StandaloneDeriving
100− , RecordWildCards
101− , ViewPatterns
60+ DeriveAnyClass
61+ DeriveDataTypeable
62+ DeriveGeneric
63+ DerivingStrategies
64+ GeneralizedNewtypeDeriving
65+ LambdaCase
66+ NamedFieldPuns
67+ OverloadedStrings
68+ PatternSynonyms
69+ StandaloneDeriving
70+ RecordWildCards
71+ ViewPatterns
10272 if impl(ghc >= 9.2)
10373 default-extensions: NoFieldSelectors
104− ghc-options: -Wall -Wextra
105−
106−test-suite spec_test
107− type: exitcode-stdio-1.0
108− build-depends:
109− , base
110− , bytestring
111− , containers
112− , hspec >= 2.10.8 && < 2.12
113− , libphonenumber
114− , QuickCheck >= 2.13.1 && < 2.16
115− main-is: Spec.hs
116− hs-source-dirs: test
117− default-language: Haskell2010
118− ghc-options: -Wall -Wextra
74+ ghc-options: -Wall -Wextra -Werror
11975
12076test-suite leak_test
12177 type: exitcode-stdio-1.0
12278 build-depends:
123− , base
79+ base
12480 , libphonenumber
125− , process >= 1.6.5 && < 1.7
81+ , process
12682 main-is: Leak.hs
12783 hs-source-dirs: test
12884 default-language: Haskell2010
129− ghc-options: -Wall -Wextra
src/Data/PhoneNumber/Internal/Number.chs+1−1
@@ -126,7 +126,7 @@ acquireCPhoneNumber p = do
126126-- , 'Data.PhoneNumber.Number.numberOfLeadingZeros' :: !('Maybe' 'Int')
127127-- }
128128-- @
129−{#pointer *CxxPhoneNumber as PhoneNumber foreign finalizer c_phone_number_free newtype#}
129+{#pointer *PhoneNumber foreign finalizer c_phone_number_free newtype#}
130130
131131{#fun unsafe c_phone_number_marshal
132132 { withCPhoneNumber* `CPhoneNumber'
src/Data/PhoneNumber/Number.hs+29−57
@@ -21,10 +21,16 @@ module Data.PhoneNumber.Number
2121 ) where
2222
2323import Data.ByteString (ByteString)
24−import Data.PhoneNumber.Internal.Number
25− ( CPhoneNumber(CPhoneNumber), PhoneNumber, CountryCodeSource, toCPhoneNumber
26− , fromCPhoneNumber
27− )
24+import Data.PhoneNumber.Internal.Number hiding
25+ ( pattern PhoneNumber
26+ , extension
27+ , rawInput
28+ , preferredDomesticCarrierCode
29+ , nationalNumber
30+ , countryCode
31+ , italianLeadingZero
32+ , countryCodeSource
33+ , numberOfLeadingZeros )
2834import qualified Data.PhoneNumber.Internal.Number as I
2935
3036-- | A country calling code (International Subscriber Dialing code, ISD code),
@@ -38,8 +44,8 @@ newtype CountryCode = CountryCode Int
3844 deriving newtype (Eq, Ord, Show, Num)
3945
4046-- | Record pattern synonym for accessing data fields of the underlying C++
41−-- object. It can be used for record construction, record update, and record
42−-- pattern match. See t'PhoneNumber'.
47+-- object. If on GHC 9.2+, it can be used for record construction, record
48+-- update, and record pattern match. See t'PhoneNumber'.
4349pattern PhoneNumber
4450 :: Maybe ByteString
4551 -> Maybe ByteString
@@ -51,6 +57,7 @@ pattern PhoneNumber
5157 -> Maybe Int
5258 -> PhoneNumber
5359pattern PhoneNumber
60+#if __GLASGOW_HASKELL__ >= 902
5461 { extension
5562 , rawInput
5663 , preferredDomesticCarrierCode
@@ -60,6 +67,10 @@ pattern PhoneNumber
6067 , countryCodeSource
6168 , numberOfLeadingZeros
6269 }
70+#else
71+ extension rawInput preferredDomesticCarrierCode nationalNumber countryCode
72+ italianLeadingZero countryCodeSource numberOfLeadingZeros
73+#endif
6374 <- (toCPhoneNumber -> CPhoneNumber
6475 { I.nationalNumber = fromIntegral -> nationalNumber
6576 , I.countryCode = fromIntegral -> CountryCode -> countryCode
@@ -72,69 +83,30 @@ pattern PhoneNumber
7283 countryCodeSource (fmap fromIntegral -> numberOfLeadingZeros)
7384 = fromCPhoneNumber $ CPhoneNumber{..}
7485
75−{-# COMPLETE PhoneNumber #-}
76−
77−-- | E.g.:
78−--
79−-- @
80−-- 'Data.PhoneNumber.Util.parseNumber' 'Data.PhoneNumber.Util.Canonicalize' 'Nothing' "+1 800-234-5678 ext. 1234"
81−-- = 'Right' v'PhoneNumber' { 'extension' = 'Just' "1234", .. }
82−-- @
86+#if __GLASGOW_HASKELL__ < 902
8387extension :: PhoneNumber -> Maybe ByteString
88+extension (PhoneNumber x _ _ _ _ _ _ _) = x
8489
85−-- | E.g.:
86−--
87−-- @
88−-- 'Data.PhoneNumber.Util.parseNumber' 'Data.PhoneNumber.Util.KeepRawInput' 'Nothing' " + 1(2-3~4*5.6 "
89−-- = 'Right' v'PhoneNumber' { 'rawInput' = 'Just' " + 1(2-3~4*5.6 ", .. }
90−-- @
9190rawInput :: PhoneNumber -> Maybe ByteString
91+rawInput (PhoneNumber _ x _ _ _ _ _ _) = x
9292
93−-- | E.g.:
94−--
95−-- @
96−-- 'Data.PhoneNumber.Util.parseNumber' 'Data.PhoneNumber.Util.KeepRawInput' ('Just' \"BR\") "0 41 (21) 2345-6789"
97−-- = 'Right' v'PhoneNumber' { 'preferredDomesticCarrierCode' = 'Just' "41", .. }
98−-- @
9993preferredDomesticCarrierCode :: PhoneNumber -> Maybe ByteString
94+preferredDomesticCarrierCode (PhoneNumber _ _ x _ _ _ _ _) = x
10095
101−-- | You probably want to use 'Data.PhoneNumber.Util.nationalSignificantNumber'
102−-- instead. E.g.:
103−--
104−-- @
105−-- 'Data.PhoneNumber.Util.parseNumber' 'Data.PhoneNumber.Util.Canonicalize' 'Nothing' "+800 0001 2345"
106−-- = 'Right' v'PhoneNumber' { 'nationalNumber' = 12345, 'numberOfLeadingZeros' = Just 3, .. }
107−-- @
10896nationalNumber :: PhoneNumber -> Word
97+nationalNumber (PhoneNumber _ _ _ x _ _ _ _) = x
10998
110−-- | E.g.:
111−--
112−-- @
113−-- 'Data.PhoneNumber.Util.parseNumber' 'Data.PhoneNumber.Util.Canonicalize' ('Just' \"US\") "800-234-5678"
114−-- = 'Right' v'PhoneNumber' { 'countryCode' = 1, .. }
115−-- @
11699countryCode :: PhoneNumber -> CountryCode
100+countryCode (PhoneNumber _ _ _ _ x _ _ _) = x
117101
118−-- | E.g.:
119−--
120−-- @
121−-- 'Data.PhoneNumber.Util.parseNumber' 'Data.PhoneNumber.Util.Canonicalize' 'Nothing' "+39 06 1234 5678"
122−-- = 'Right' v'PhoneNumber' { 'italianLeadingZero' = 'Just' 'True', .. }
123−-- @
124102italianLeadingZero :: PhoneNumber -> Maybe Bool
103+italianLeadingZero (PhoneNumber _ _ _ _ _ x _ _) = x
125104
126−-- | E.g.:
127−--
128−-- @
129−-- 'Data.PhoneNumber.Util.parseNumber' 'Data.PhoneNumber.Util.KeepRawInput' ('Just' \"US\") "011 800 1234 5678"
130−-- = 'Right' v'PhoneNumber' { 'countryCodeSource' = 'Just' 'FromNumberWithIdd', .. }
131−-- @
132105countryCodeSource :: PhoneNumber -> Maybe CountryCodeSource
106+countryCodeSource (PhoneNumber _ _ _ _ _ _ x _) = x
133107
134−-- | E.g.:
135−--
136−-- @
137−-- 'Data.PhoneNumber.Util.parseNumber' 'Data.PhoneNumber.Util.Canonicalize' 'Nothing' "+800 0001 2345"
138−-- = 'Right' v'PhoneNumber' { 'numberOfLeadingZeros' = Just 3, 'nationalNumber' = 12345, .. }
139−-- @
140108numberOfLeadingZeros :: PhoneNumber -> Maybe Int
109+numberOfLeadingZeros (PhoneNumber _ _ _ _ _ _ _ x) = x
110+#endif
111+
112+{-# COMPLETE PhoneNumber #-}
src/Data/PhoneNumber/Util.hs+51−72
@@ -29,7 +29,7 @@ module Data.PhoneNumber.Util
2929 , supportedTypesForNonGeoEntity
3030 -- * 'Region'/'CountryCode' Queries
3131 , Region(..)
32− , NonGeoRegion(..)
32+ , noRegion
3333 , CountryCode(..)
3434 , countryCodeForRegion
3535 , regionForCountryCode
@@ -59,31 +59,19 @@ import GHC.Generics
5959import GHC.Exts
6060import GHC.IO
6161
62−-- | An ISO 3166-1 alpha-2 country code in upper case.
62+-- | Most of the time an ISO 3166-1 alpha-2 country code in upper case, except
63+-- in some cases the UN M.49 code @"001"@ (meaning the world) used to represent
64+-- non-geographical entities.
65+--
66+-- In some situations, the 'noRegion' value corresponds to a default or
67+-- unspecified region.
6368newtype Region = Region ByteString
6469 deriving newtype (Eq, Ord, Show, Read, IsString, NFData)
6570 deriving stock (Data, Generic)
6671
67−-- | A "region" corresponding to non-geographical entities. The library
68−-- internally uses the UN M.49 code @"001"@ (meaning the world) for this.
69−data NonGeoRegion = Region001
70− deriving stock (Eq, Ord, Show, Read, Data, Generic)
71− deriving anyclass (NFData)
72−
73−filter001 :: ByteString -> Either NonGeoRegion Region
74−filter001 region
75− | region == "001" = Left Region001
76− | otherwise = Right $ Region region
77−
78−filter001ZZ :: ByteString -> Maybe (Either NonGeoRegion Region)
79−filter001ZZ region
80− | region == "ZZ" = Nothing
81− | region == "001" = Just $ Left Region001
82− | otherwise = Just $ Right $ Region region
83−
84−filter0 :: Int -> Maybe CountryCode
85−filter0 0 = Nothing
86−filter0 cc = Just $ CountryCode cc
72+-- | A default or unspecified region, the user-assigned alpha-2 code @\"ZZ\"@
73+noRegion :: Region
74+noRegion = "ZZ"
8775
8876-- | All geographical regions the library has metadata for
8977supportedRegions :: S.Set Region
@@ -93,8 +81,8 @@ supportedRegions = S.fromList $ unsafeDupablePerformIO $ mask_ $
9381-- | All global network calling codes (country calling codes for
9482-- non-geographical entities) the library has metadata for
9583supportedGlobalNetworkCallingCodes :: S.Set CountryCode
96−supportedGlobalNetworkCallingCodes = S.fromList $ map (CountryCode . fromIntegral) $
97− unsafeDupablePerformIO $ mask_ $
84+supportedGlobalNetworkCallingCodes = S.fromList $ unsafeDupablePerformIO $ mask_ $
85+ map (CountryCode . fromIntegral) <$>
9886 c_phone_number_util_get_supported_global_network_calling_codes
9987
10088-- | All country calling codes the library has metadata for, covering both
@@ -102,8 +90,8 @@ supportedGlobalNetworkCallingCodes = S.fromList $ map (CountryCode . fromIntegra
10290-- geographical entities. This could be used to populate a drop-down box of
10391-- country calling codes for a phone-number widget, for instance.
10492supportedCallingCodes :: S.Set CountryCode
105−supportedCallingCodes = S.fromList $ map (CountryCode . fromIntegral) $
106− unsafeDupablePerformIO $ mask_ $
93+supportedCallingCodes = S.fromList $ unsafeDupablePerformIO $ mask_ $
94+ map (CountryCode . fromIntegral) <$>
10795 c_phone_number_util_get_supported_calling_codes
10896
10997-- | Returns the types for a given region which the library has metadata for.
@@ -113,9 +101,8 @@ supportedCallingCodes = S.fromList $ map (CountryCode . fromIntegral) $
113101--
114102-- No types will be returned for invalid or unknown region codes.
115103supportedTypesForRegion :: Region -> S.Set PhoneNumberType
116−supportedTypesForRegion (Region region) = S.fromList $
117− unsafeDupablePerformIO $ mask_ $
118− c_phone_number_util_get_supported_types_for_region region
104+supportedTypesForRegion (Region region) = S.fromList $ unsafeDupablePerformIO $ mask_ $
105+ c_phone_number_util_get_supported_types_for_region region
119106
120107-- | Returns the types for a country-code belonging to a non-geographical entity
121108-- which the library has metadata for. Will not include 'FixedLineOrMobile' (if
@@ -126,9 +113,8 @@ supportedTypesForRegion (Region region) = S.fromList $
126113-- No types will be returned for country calling codes that do not map to a
127114-- known non-geographical entity.
128115supportedTypesForNonGeoEntity :: CountryCode -> S.Set PhoneNumberType
129−supportedTypesForNonGeoEntity (CountryCode cc) = S.fromList $
130− unsafeDupablePerformIO $ mask_ $
131− c_phone_number_util_get_supported_types_for_non_geo_entity $ fromIntegral cc
116+supportedTypesForNonGeoEntity (CountryCode cc) = S.fromList $ unsafeDupablePerformIO $ mask_ $
117+ c_phone_number_util_get_supported_types_for_non_geo_entity $ fromIntegral cc
132118
133119-- | Returns true if the number is a valid vanity (alpha) number such as
134120-- @"800 MICROSOFT"@. A valid vanity number will start with at least 3 digits
@@ -289,27 +275,24 @@ numberType pn = unsafeDupablePerformIO $
289275-- @+41 (0) 78 927 2696@ can be parsed into a number with country code @"41"@
290276-- and National Significant Number @"789272696"@. This is valid, while the
291277-- original string is not dialable.
292−isValidNumber :: Maybe (Either NonGeoRegion Region) -> PhoneNumber -> Bool
278+isValidNumber :: Maybe Region -> PhoneNumber -> Bool
293279isValidNumber Nothing pn = unsafeDupablePerformIO $
294280 c_phone_number_util_is_valid_number pn
295−isValidNumber (Just (Left Region001)) pn = unsafeDupablePerformIO $
296− c_phone_number_util_is_valid_number_for_region pn "001"
297−isValidNumber (Just (Right (Region region))) pn = unsafeDupablePerformIO $
281+isValidNumber (Just (Region region)) pn = unsafeDupablePerformIO $
298282 c_phone_number_util_is_valid_number_for_region pn region
299283
300284-- | Returns the region where a phone number is from. This could be used for
301285-- geocoding at the region level. Only guarantees correct results for valid,
302286-- full numbers (not short-codes, or invalid numbers).
303−regionForNumber :: PhoneNumber -> Maybe (Either NonGeoRegion Region)
304−regionForNumber pn = filter001ZZ $
305− unsafeDupablePerformIO $ mask_ $
306− c_phone_number_util_get_region_code_for_number pn
287+regionForNumber :: PhoneNumber -> Region
288+regionForNumber pn = unsafeDupablePerformIO $ mask_ $
289+ coerce $ c_phone_number_util_get_region_code_for_number pn
307290
308291-- | Returns the country calling code for a specific region. For example, this
309292-- would be @1@ for the United States, and @64@ for New Zealand.
310−countryCodeForRegion :: Region -> Maybe CountryCode
311−countryCodeForRegion (Region region) = filter0 $ fromIntegral $
312− unsafeDupablePerformIO $
293+countryCodeForRegion :: Region -> CountryCode
294+countryCodeForRegion (Region region) = unsafeDupablePerformIO $
295+ CountryCode . fromIntegral <$>
313296 c_phone_number_util_get_country_code_for_region region
314297
315298-- | Returns the region code that matches the specific country code. Note that
@@ -317,19 +300,18 @@ countryCodeForRegion (Region region) = filter0 $ fromIntegral $
317300-- (e.g. US and Canada), and in that case, only one of the regions (normally the
318301-- one with the largest population) is returned. If the country calling code
319302-- entered is valid but doesn't match a specific region (such as in the case of
320−-- non-geographical calling codes like @800@) @'Region001'@ will be returned.
321−regionForCountryCode :: CountryCode -> Maybe (Either NonGeoRegion Region)
322−regionForCountryCode (CountryCode cc) = filter001ZZ $
323− unsafeDupablePerformIO $ mask_ $
324− c_phone_number_util_get_region_code_for_country_code $ fromIntegral cc
303+-- non-geographical calling codes like @800@) the @'Region' "001"@ will be
304+-- returned.
305+regionForCountryCode :: CountryCode -> Region
306+regionForCountryCode (CountryCode cc) = unsafeDupablePerformIO $ mask_ $
307+ coerce $ c_phone_number_util_get_region_code_for_country_code $ fromIntegral cc
325308
326309-- | Returns a list of the region codes that match the specific country calling
327−-- code. For non-geographical country calling codes, 'Region001' is returned.
328−-- Also, in the case of no region code being found, the list is empty.
329−regionsForCountryCode :: CountryCode -> [Either NonGeoRegion Region]
330−regionsForCountryCode (CountryCode cc) = map filter001 $
331− unsafeDupablePerformIO $ mask_ $
332− c_phone_number_util_get_region_codes_for_country_calling_code $ fromIntegral cc
310+-- code. For non-geographical country calling codes, the region code @"001"@ is
311+-- returned. Also, in the case of no region code being found, the list is empty.
312+regionsForCountryCode :: CountryCode -> [Region]
313+regionsForCountryCode (CountryCode cc) = unsafeDupablePerformIO $ mask_ $
314+ coerce $ c_phone_number_util_get_region_codes_for_country_calling_code $ fromIntegral cc
333315
334316-- | Checks if this is a region under the North American Numbering Plan
335317-- Administration (NANPA).
@@ -371,12 +353,12 @@ nddPrefixForRegion strip (Region region) = unsafeDupablePerformIO $ mask_ $
371353-- 3. For some numbers (particularly fixed-line), many regions have the concept
372354-- of area code, which together with subscriber number constitute the National
373355-- Significant Number. It is sometimes okay to dial only the subscriber number
374−-- when dialing in the same area. This function will return
375−-- 'IsPossibleLocalOnly' if the subscriber-number-only version is passed in. On
376−-- the other hand, because 'isValidNumber' validates using information on both
377−-- starting digits (for fixed line numbers, that would most likely be area
378−-- codes) and length (obviously includes the length of area codes for fixed line
379−-- numbers), it will return 'False' for the subscriber-number-only version.
356+-- when dialing in the same area. This function will return IsPossibleLocalOnly
357+-- if the subscriber-number-only version is passed in. On the other hand,
358+-- because 'isValidNumber' validates using information on both starting digits
359+-- (for fixed line numbers, that would most likely be area codes) and length
360+-- (obviously includes the length of area codes for fixed line numbers), it will
361+-- return 'False' for the subscriber-number-only version.
380362possibleNumber :: PhoneNumberType -> PhoneNumber -> ValidationResult
381363possibleNumber ntype pn = unsafeDupablePerformIO $
382364 c_phone_number_util_is_possible_number_for_type_with_reason pn ntype
@@ -445,20 +427,20 @@ data ParseMode
445427-- 'isValidNumber'.
446428--
447429-- Returns an error if the string is not considered to be a viable phone number
448−-- (e.g. too few or too many digits) or if no 'Region' was supplied and the
430+-- (e.g. too few or too many digits) or if 'noRegion' was supplied and the
449431-- number is not in international format (does not start with @\'+\'@).
450432parseNumber
451433 :: ParseMode
452− -> Maybe Region -- ^ Default region, the country that we are expecting the
453− -- number to be dialed from, which affects national and international
454− -- dialing prefixes. This is only used if the number being parsed is not
455− -- written in international format. In such cases the 'countryCode' of the
456− -- number would be that of the default region supplied. If the number is
457− -- guaranteed to start with a @\'+\'@ followed by the country calling code,
458− -- then this can be omitted.
434+ -> Region -- ^ Default region, the country that we are expecting the number to
435+ -- be dialed from, which affects national and international dialing
436+ -- prefixes. This is only used if the number being parsed is not written in
437+ -- international format. In such cases the 'countryCode' of the number would
438+ -- be that of the default region supplied. If the number is guaranteed to
439+ -- start with a @\'+\'@ followed by the country calling code, then
440+ -- 'noRegion' can be supplied.
459441 -> ByteString -- ^ Input.
460442 -> Either ErrorType PhoneNumber
461−parseNumber mode mRegion number = case err of
443+parseNumber mode (Region region) number = case err of
462444 I.NoParsingError -> Right pn
463445 I.InvalidCountryCodeError -> Left InvalidCountryCodeError
464446 I.NotANumber -> Left NotANumber
@@ -466,9 +448,6 @@ parseNumber mode mRegion number = case err of
466448 I.TooShortNsn -> Left TooShortNsn
467449 I.TooLongNsn -> Left TooLongNsn
468450 where
469− region = case mRegion of
470− Nothing -> "ZZ"
471− Just (Region reg) -> reg
472451 (err, pn) = case mode of
473452 Canonicalize -> unsafeDupablePerformIO $ mask_ $
474453 c_phone_number_util_parse number region
test/Leak.hs+6−6
@@ -74,7 +74,7 @@ main = do
7474 carrierCode = "10"
7575 nonGeoCode = 800
7676 phoneString = "+41 44 668 1800"
77− Right phoneNumber = parseNumber KeepRawInput Nothing phoneString
77+ Right phoneNumber = parseNumber KeepRawInput noRegion phoneString
7878 hammer iter (\(PhoneNumber a b c d e f g h) -> PhoneNumber a b c d e f g h) phoneNumber
7979 hammer iter supportedTypesForRegion region
8080 hammer iter supportedTypesForNonGeoEntity nonGeoCode
@@ -96,7 +96,7 @@ main = do
9696 hammer iter (formatNumber (OutOfCountry region False)) phoneNumber
9797 hammer iter truncateTooLongNumber phoneNumber
9898 hammer iter numberType phoneNumber
99− hammer iter (isValidNumber (Just (Right region))) phoneNumber
99+ hammer iter (isValidNumber (Just region)) phoneNumber
100100 hammer iter regionForNumber phoneNumber
101101 hammer iter countryCodeForRegion region
102102 hammer iter regionForCountryCode countryCallingCode
@@ -107,10 +107,10 @@ main = do
107107 hammer iter canBeInternationallyDialed phoneNumber
108108 hammer iter isGeographicalNumber phoneNumber
109109 hammer iter (isGeographicalNumberType FixedLine) countryCallingCode
110− hammer iter (parseNumber Canonicalize (Just region)) phoneString
111− hammer iter (parseNumber Canonicalize (Just region)) ""
112− hammer iter (parseNumber KeepRawInput (Just region)) phoneString
113− hammer iter (parseNumber KeepRawInput (Just region)) ""
110+ hammer iter (parseNumber Canonicalize region) phoneString
111+ hammer iter (parseNumber Canonicalize region) ""
112+ hammer iter (parseNumber KeepRawInput region) phoneString
113+ hammer iter (parseNumber KeepRawInput region) ""
114114 hammer iter (matchNumbers (Left phoneString)) (Left phoneString)
115115 hammer iter (matchNumbers (Left phoneString)) (Right phoneNumber)
116116 hammer iter (matchNumbers (Right phoneNumber)) (Left phoneString)
test/Spec.hsdeleted−245
@@ -1,245 +0,0 @@
1−{-# LANGUAGE DerivingStrategies #-}
2−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3−{-# LANGUAGE OverloadedStrings #-}
4−import Control.Applicative
5−import Control.Monad
6−import qualified Data.ByteString as BS
7−import qualified Data.ByteString.Char8 as BSC
8−import Data.Either
9−import Data.PhoneNumber.Number
10−import Data.PhoneNumber.Util
11−import qualified Data.Set as S
12−import Test.Hspec
13−import Test.Hspec.QuickCheck
14−import Test.QuickCheck
15−
16−newtype CC = CC CountryCode
17− deriving newtype (Show)
18−
19−instance Arbitrary CC where
20− arbitrary = CC . CountryCode <$> choose (-10, 1000)
21− shrink (CC (CountryCode cc)) = CC . CountryCode <$> shrink cc
22−
23−newtype Reg = Reg Region
24− deriving newtype (Show)
25−
26−instance Arbitrary Reg where
27− arbitrary = do
28− c1 <- elements ['A'..'Z']
29− c2 <- elements ['A'..'Z']
30− pure $ Reg $ Region $ BSC.pack [c1, c2]
31−
32−newtype MReg = MReg (Maybe Region)
33− deriving newtype (Show)
34−
35−instance Arbitrary MReg where
36− arbitrary = MReg . fmap (\(Reg r) -> r) <$> arbitrary
37− shrink (MReg (Just _)) = [MReg Nothing]
38− shrink (MReg Nothing) = []
39−
40−newtype ParserInput = ParserInput BS.ByteString
41− deriving newtype (Show)
42−
43−instance Arbitrary ParserInput where
44− arbitrary = do
45− len <- choose (0, 20)
46− xs <- replicateM len $ frequency
47− [ (10, elements "#()+-0123456789")
48− , (1, elements "'*,./:;") ]
49− pure $ ParserInput $ BSC.pack xs
50− shrink (ParserInput bs) = ParserInput . BS.pack <$> shrink (BS.unpack bs)
51−
52−data ParsedNumber = ParsedNumber (ParseMode, Maybe Region, ParserInput) PhoneNumber
53− deriving stock (Show)
54−
55−instance Arbitrary ParsedNumber where
56− arbitrary = do
57− mode <- elements [Canonicalize, KeepRawInput]
58− liftA2 ((,,) mode) arbitrary arbitrary
59− `suchThatMap` \(_, MReg mRegion, ParserInput input) ->
60− case parseNumber mode mRegion input of
61− Right pn -> Just $ ParsedNumber (mode, mRegion, ParserInput input) pn
62− _ -> Nothing
63− shrink (ParsedNumber (mode, region, input) _) = do
64− ParserInput input' <- shrink input
65− case parseNumber mode region input' of
66− Right pn -> pure $ ParsedNumber (mode, region, ParserInput input') pn
67− _ -> empty
68−
69−regZZ, reg001 :: Region
70−regZZ = "ZZ"
71−reg001 = "001"
72−
73−cc0 :: CountryCode
74−cc0 = 0
75−
76−main :: IO ()
77−main = hspec $ modifyMaxSuccess (* 10) $ do
78− describe "supportedRegions" $ do
79− prop "doesn't contain ZZ" $ regZZ `S.notMember` supportedRegions
80− prop "doesn't contain 001" $ reg001 `S.notMember` supportedRegions
81−
82− describe "supportedGlobalNetworkCallingCodes" $ do
83− prop "doesn't contain 0" $ cc0 `S.notMember` supportedGlobalNetworkCallingCodes
84− prop "is a subset of supportedCallingCodes" $
85− supportedGlobalNetworkCallingCodes `S.isSubsetOf` supportedCallingCodes
86−
87− describe "supportedCallingCodes" $ do
88− prop "doesn't contain 0" $ cc0 `S.notMember` supportedCallingCodes
89−
90− describe "supportedTypesForRegion" $ do
91− prop "doesn't contain FixedLineOrMobile" $ \(Reg region) ->
92− FixedLineOrMobile `S.notMember` supportedTypesForRegion region
93−
94− describe "supportedTypesForNonGeoEntity" $ do
95− prop "doesn't contain FixedLineOrMobile" $ \(CC cc) ->
96− FixedLineOrMobile `S.notMember` supportedTypesForNonGeoEntity cc
97−
98− describe "convertAlphaNumber" $ do
99− prop "doesn't change the parsing of a number" $ \(MReg mRegion) (ParserInput str) ->
100− parseNumber Canonicalize mRegion (convertAlphaNumber str)
101− === parseNumber Canonicalize mRegion str
102− prop "returns a number that is not alpha" $ \(ParserInput str) ->
103− not $ isAlphaNumber $ convertAlphaNumber str
104−
105− describe "normalizeNumber" $ do
106− prop "returns ASCII digits unmodified" $
107− forAll (elements [Digits, Dialable]) $ \mode ->
108− forAll (listOf $ elements ['0'..'9']) $ \str ->
109− BSC.pack str == normalizeNumber mode (BSC.pack str)
110− describe "Digits" $ do
111− prop "returns only ASCII digits" $ \str ->
112− BSC.all (`elem` ['0'..'9']) $ normalizeNumber Digits $ BSC.pack str
113− prop "doesn't randomly fail" $ \str ->
114− not (BS.null $ normalizeNumber Digits $ "0" <> BS.pack str)
115− .&&. not (BS.null $ normalizeNumber Digits $ BS.pack str <> "0")
116− describe "Dialable" $ do
117− -- PhoneNumberUtil::NormalizeDiallableCharsOnly will error on any invalid UTF8
118− prop "doesn't randomly fail" $ \str ->
119− not (BS.null $ normalizeNumber Digits $ "0" <> BS.pack str)
120− .&&. not (BS.null $ normalizeNumber Digits $ BS.pack str <> "0")
121−
122− describe "nationalSignificantNumber" $ do
123− prop "is non-empty" $ \(ParsedNumber _ pn) ->
124− not $ BS.null $ nationalSignificantNumber pn
125− prop "returns only ASCII digits" $ \(ParsedNumber _ pn) ->
126− BSC.all (`elem` ['0'..'9']) $ nationalSignificantNumber pn
127− modifyMaxDiscardRatio (* 10) $ do
128− prop "concatenated with country code parses to the original (if valid)" $
129− \(ParsedNumber _ pn) ->
130− isValidNumber Nothing pn ==>
131− let
132− cc = countryCode pn
133− nsn = nationalSignificantNumber pn
134− str = "+" <> BSC.pack (show cc) <> nsn
135− in case parseNumber Canonicalize Nothing str of
136− Left err -> error $ show err
137− Right pn' -> cc === countryCode pn'
138− .&&. nsn === nationalSignificantNumber pn'
139−
140− describe "countryMobileToken" $ do
141− prop "returns only ASCII digits" $ \(CC cc) ->
142− BSC.all (`elem` ['0'..'9']) $ countryMobileToken cc
143−
144− describe "formatNumber" $ do
145− prop "with International produces a parseable result (if no ext)" $
146− \(ParsedNumber _ pn) -> possibleNumber Unknown pn == IsPossible ==>
147− -- Apparently in the TW region an extension number is formatted
148− -- with a "#", and the formatter will format
149− -- "+886 2 2123 4567 ext. 1234567890" as "+886 2 2123 4567#1234567890"
150− -- even though the former parses and the latter doesn't
151− extension pn == Nothing ==>
152− isRight $ parseNumber Canonicalize Nothing
153− $ formatNumber International pn
154− modifyMaxDiscardRatio (* 10) $ do
155− prop "with E164 produces a parseable result (if Canonicalize)" $
156− \(ParsedNumber (mode, _, _) pn) ->
157− -- KeepRawInput with pn == "0000" breaks E164 formatting
158− possibleNumber Unknown pn == IsPossible && mode == Canonicalize ==>
159− isRight $ parseNumber Canonicalize Nothing
160− $ formatNumber E164 pn
161− prop "with RFC3966 produces a parseable result (if Canonicalize)" $
162− \(ParsedNumber (mode, _, _) pn) ->
163− -- KeepRawInput with pn == "0000" breaks RFC3966 formatting
164− possibleNumber Unknown pn == IsPossible && mode == Canonicalize ==>
165− isRight $ parseNumber Canonicalize Nothing
166− $ formatNumber RFC3966 pn
167−
168− describe "truncateTooLongNumber" $ do
169− pure ()
170−
171− describe "numberType" $ do
172− pure ()
173−
174− describe "isValidNumber" $ do
175− modifyMaxDiscardRatio (* 10) $ do
176− prop "is preserved under parse . format" $ \(ParsedNumber _ pn) ->
177− isValidNumber Nothing pn ==>
178− case parseNumber Canonicalize Nothing $ formatNumber E164 pn of
179− Left err -> error $ show err
180− Right pn' -> isValidNumber Nothing pn'
181−
182− describe "regionForNumber" $ do
183− prop "doesn't return ZZ" $ \(ParsedNumber _ pn) ->
184− regionForNumber pn =/= Just (Right regZZ)
185− prop "doesn't return 001" $ \(ParsedNumber _ pn) ->
186− regionForNumber pn =/= Just (Right reg001)
187− prop "returns a supported region" $ \(ParsedNumber _ pn) ->
188− case regionForNumber pn of
189− Just (Right region) -> region `S.member` supportedRegions
190− _ -> discard
191− prop "returns Region001 exactly for supported non-geo entities" $
192− \(ParsedNumber _ pn) ->
193− (countryCode pn `S.member` supportedGlobalNetworkCallingCodes)
194− === (regionForNumber pn == Just (Left Region001))
195−
196− describe "countryCodeForRegion" $ do
197− prop "doesn't return 0" $ \(Reg region) ->
198− countryCodeForRegion region =/= Just cc0
199− prop "returns codes in supportedCallingCodes" $ \(Reg region) ->
200− case countryCodeForRegion region of
201− Nothing -> discard
202− Just cc -> cc `S.member` supportedCallingCodes
203−
204− describe "regionForCountryCode" $ do
205− prop "doesn't return ZZ" $ \(CC cc) ->
206− regionForCountryCode cc =/= Just (Right regZZ)
207− prop "doesn't return 001" $ \(CC cc) ->
208− regionForCountryCode cc =/= Just (Right reg001)
209− prop "returns Region001 exactly for supported non-geo entities" $ \(CC cc) ->
210− (cc `S.member` supportedGlobalNetworkCallingCodes)
211− === (regionForCountryCode cc == Just (Left Region001))
212−
213− describe "regionsForCountryCode" $ do
214− prop "doesn't return ZZ" $ \(CC cc) ->
215− Right regZZ `notElem` regionsForCountryCode cc
216− prop "doesn't return 001" $ \(CC cc) ->
217− Right reg001 `notElem` regionsForCountryCode cc
218−
219− describe "isNANPACountry" $ do
220− pure ()
221−
222− describe "nddPrefixForRegion" $ do
223− prop "with True returns only ASCII digits" $ \(Reg region) ->
224− BSC.all (`elem` ['0'..'9']) $ nddPrefixForRegion True region
225−
226− describe "possibleNumber" $ do
227− modifyMaxDiscardRatio (* 10) $ do
228− prop "with Unknown is more lenient than isValidNumber" $
229− \(ParsedNumber _ pn) -> isValidNumber Nothing pn ==>
230− possibleNumber Unknown pn `elem` [IsPossible, IsPossibleLocalOnly]
231−
232− describe "canBeInternationallyDialed" $ do
233− pure ()
234−
235− describe "isGeographicalNumber" $ do
236− prop "matches isGeographicalNumberType" $ \(ParsedNumber _ pn) ->
237− isGeographicalNumber pn
238− === isGeographicalNumberType (numberType pn) (countryCode pn)
239−
240− describe "isGeographicalNumberType" $ do
241− pure ()
242−
243− describe "parseNumber" $ do
244− prop "doesn't return a number with CountryCode 0" $ \(ParsedNumber _ pn) ->
245− countryCode pn /= cc0