git.iko.soy/typeable/libphonenumber
on main help sign in
12 files changed+349−145
since splitsnapshots
unifiedsplit
.github/workflows/ci.yaml+102 LICENSE+1−1 cxxbits/c_phonenumber.cpp+6−4 cxxbits/c_phonenumberutil.cpp+45−26 include/c_phonenumber.h+4−8 include/c_phonenumberutil.h+20−20 include/common.h+3−2 libphonenumber.cabal+58−40 src/Data/PhoneNumber/Internal/Number.chs+1−1 src/Data/PhoneNumber/Number.hs+69−10 src/Data/PhoneNumber/Util.hs+6−6 test/Spec.hs+34−27
.github/workflows/ci.yamlnew file+102
@@ -0,0 +1,102 @@
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"
LICENSE+1−1
@@ -1,6 +1,6 @@
11MIT License
22
3−Copyright (c) 2021 Typeable
3+Copyright (c) 2023 Typeable
44
55Permission is hereby granted, free of charge, to any person obtaining a copy
66of this software and associated documentation files (the "Software"), to deal
cxxbits/c_phonenumber.cpp+6−4
@@ -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" PhoneNumber *c_phone_number_marshal(phone_number const *c_number)
58+extern "C" CxxPhoneNumber *c_phone_number_marshal(phone_number const *c_number)
5959{
6060 PhoneNumber *number = new PhoneNumber();
6161 if (c_number->extension != nullptr)
@@ -72,11 +72,12 @@ extern "C" PhoneNumber *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 number;
75+ return reinterpret_cast<CxxPhoneNumber *>(number);
7676}
7777
78−extern "C" void c_phone_number_unmarshal(PhoneNumber const *number, phone_number *c_number)
78+extern "C" void c_phone_number_unmarshal(CxxPhoneNumber const *ptr, phone_number *c_number)
7979{
80+ auto number = reinterpret_cast<PhoneNumber const *>(ptr);
8081 if (number->has_extension())
8182 unmarshal_string_size(number->extension(), c_number->extension, c_number->extension_size);
8283 else
@@ -99,7 +100,8 @@ extern "C" void c_phone_number_unmarshal(PhoneNumber const *number, phone_number
99100 c_number->number_of_leading_zeros = number->number_of_leading_zeros();
100101}
101102
102−extern "C" void c_phone_number_free(PhoneNumber *number)
103+extern "C" void c_phone_number_free(CxxPhoneNumber *ptr)
103104{
105+ auto number = reinterpret_cast<PhoneNumber *>(ptr);
104106 delete number;
105107}
cxxbits/c_phonenumberutil.cpp+45−26
@@ -310,10 +310,11 @@ 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− PhoneNumber const *number,
313+ CxxPhoneNumber const *ptr,
314314 c_string *c_nsn
315315)
316316{
317+ auto number = reinterpret_cast<PhoneNumber const *>(ptr);
317318 std::string nsn;
318319 PhoneNumberUtil::GetInstance()->GetNationalSignificantNumber(*number, &nsn);
319320 unmarshal_c_string(nsn, *c_nsn);
@@ -330,23 +331,25 @@ extern "C" void c_phone_number_util_get_country_mobile_token(
330331}
331332
332333extern "C" void c_phone_number_util_format(
333− PhoneNumber const *number,
334+ CxxPhoneNumber const *ptr,
334335 phone_number_format format,
335336 c_string *c_string
336337)
337338{
339+ auto number = reinterpret_cast<PhoneNumber const *>(ptr);
338340 std::string string;
339341 PhoneNumberUtil::GetInstance()->Format(*number, marshal_phone_number_format(format), &string);
340342 unmarshal_c_string(string, *c_string);
341343}
342344
343345extern "C" void c_phone_number_util_format_national_number_with_carrier_code(
344− PhoneNumber const *number,
346+ CxxPhoneNumber const *ptr,
345347 char const *c_carrier_code,
346348 size_t c_carrier_code_size,
347349 c_string *c_string
348350)
349351{
352+ auto number = reinterpret_cast<PhoneNumber const *>(ptr);
350353 std::string const carrier_code(c_carrier_code, c_carrier_code_size);
351354 std::string string;
352355 PhoneNumberUtil::GetInstance()->FormatNationalNumberWithCarrierCode(*number, carrier_code, &string);
@@ -354,12 +357,13 @@ extern "C" void c_phone_number_util_format_national_number_with_carrier_code(
354357}
355358
356359extern "C" void c_phone_number_util_format_national_number_with_preferred_carrier_code(
357− PhoneNumber const *number,
360+ CxxPhoneNumber const *ptr,
358361 char const *c_carrier_code,
359362 size_t c_carrier_code_size,
360363 c_string *c_string
361364)
362365{
366+ auto number = reinterpret_cast<PhoneNumber const *>(ptr);
363367 std::string const carrier_code(c_carrier_code, c_carrier_code_size);
364368 std::string string;
365369 PhoneNumberUtil::GetInstance()->FormatNationalNumberWithPreferredCarrierCode(*number, carrier_code, &string);
@@ -367,13 +371,14 @@ extern "C" void c_phone_number_util_format_national_number_with_preferred_carrie
367371}
368372
369373extern "C" void c_phone_number_util_format_number_for_mobile_dialing(
370− PhoneNumber const *number,
374+ CxxPhoneNumber const *ptr,
371375 char const *c_region,
372376 size_t c_region_size,
373377 int with_formatting,
374378 c_string *c_string
375379)
376380{
381+ auto number = reinterpret_cast<PhoneNumber const *>(ptr);
377382 std::string const region(c_region, c_region_size);
378383 std::string string;
379384 PhoneNumberUtil::GetInstance()->FormatNumberForMobileDialing(*number, region, with_formatting, &string);
@@ -381,12 +386,13 @@ extern "C" void c_phone_number_util_format_number_for_mobile_dialing(
381386}
382387
383388extern "C" void c_phone_number_util_format_out_of_country_calling_number(
384− PhoneNumber const *number,
389+ CxxPhoneNumber const *ptr,
385390 char const *c_region,
386391 size_t c_region_size,
387392 c_string *c_string
388393)
389394{
395+ auto number = reinterpret_cast<PhoneNumber const *>(ptr);
390396 std::string const region(c_region, c_region_size);
391397 std::string string;
392398 PhoneNumberUtil::GetInstance()->FormatOutOfCountryCallingNumber(*number, region, &string);
@@ -394,12 +400,13 @@ extern "C" void c_phone_number_util_format_out_of_country_calling_number(
394400}
395401
396402extern "C" void c_phone_number_util_format_in_original_format(
397− PhoneNumber const *number,
403+ CxxPhoneNumber const *ptr,
398404 char const *c_region,
399405 size_t c_region_size,
400406 c_string *c_string
401407)
402408{
409+ auto number = reinterpret_cast<PhoneNumber const *>(ptr);
403410 std::string const region(c_region, c_region_size);
404411 std::string string;
405412 PhoneNumberUtil::GetInstance()->FormatInOriginalFormat(*number, region, &string);
@@ -407,24 +414,26 @@ extern "C" void c_phone_number_util_format_in_original_format(
407414}
408415
409416extern "C" void c_phone_number_util_format_out_of_country_keeping_alpha_chars(
410− PhoneNumber const *number,
417+ CxxPhoneNumber const *ptr,
411418 char const *c_region,
412419 size_t c_region_size,
413420 c_string *c_string
414421)
415422{
423+ auto number = reinterpret_cast<PhoneNumber const *>(ptr);
416424 std::string const region(c_region, c_region_size);
417425 std::string string;
418426 PhoneNumberUtil::GetInstance()->FormatOutOfCountryKeepingAlphaChars(*number, region, &string);
419427 unmarshal_c_string(string, *c_string);
420428}
421429
422−extern "C" PhoneNumber *c_phone_number_util_truncate_too_long_number(PhoneNumber const *input)
430+extern "C" CxxPhoneNumber *c_phone_number_util_truncate_too_long_number(CxxPhoneNumber const *ptr)
423431{
432+ auto input = reinterpret_cast<PhoneNumber const *>(ptr);
424433 auto *output = new PhoneNumber(*input);
425434 if (PhoneNumberUtil::GetInstance()->TruncateTooLongNumber(output))
426435 {
427− return output;
436+ return reinterpret_cast<CxxPhoneNumber *>(output);
428437 }
429438 else
430439 {
@@ -433,33 +442,37 @@ extern "C" PhoneNumber *c_phone_number_util_truncate_too_long_number(PhoneNumber
433442 }
434443}
435444
436−extern "C" phone_number_type c_phone_number_util_get_number_type(PhoneNumber const *number)
445+extern "C" phone_number_type c_phone_number_util_get_number_type(CxxPhoneNumber const *ptr)
437446{
447+ auto number = reinterpret_cast<PhoneNumber const *>(ptr);
438448 return unmarshal_phone_number_type(
439449 PhoneNumberUtil::GetInstance()->GetNumberType(*number)
440450 );
441451}
442452
443−extern "C" int c_phone_number_util_is_valid_number(PhoneNumber const *number)
453+extern "C" int c_phone_number_util_is_valid_number(CxxPhoneNumber const *ptr)
444454{
455+ auto number = reinterpret_cast<PhoneNumber const *>(ptr);
445456 return PhoneNumberUtil::GetInstance()->IsValidNumber(*number);
446457}
447458
448459extern "C" int c_phone_number_util_is_valid_number_for_region(
449− PhoneNumber const *number,
460+ CxxPhoneNumber const *ptr,
450461 char const *c_region,
451462 size_t c_region_size
452463)
453464{
465+ auto number = reinterpret_cast<PhoneNumber const *>(ptr);
454466 std::string const region(c_region, c_region_size);
455467 return PhoneNumberUtil::GetInstance()->IsValidNumberForRegion(*number, region);
456468}
457469
458470extern "C" void c_phone_number_util_get_region_code_for_number(
459− PhoneNumber const *number,
471+ CxxPhoneNumber const *ptr,
460472 c_string *c_region
461473)
462474{
475+ auto number = reinterpret_cast<PhoneNumber const *>(ptr);
463476 std::string region;
464477 PhoneNumberUtil::GetInstance()->GetRegionCodeForNumber(*number, &region);
465478 unmarshal_c_string(region, *c_region);
@@ -518,10 +531,11 @@ extern "C" void c_phone_number_util_get_ndd_prefix_for_region(
518531}
519532
520533extern "C" validation_result c_phone_number_util_is_possible_number_for_type_with_reason(
521− PhoneNumber const *number,
534+ CxxPhoneNumber const *ptr,
522535 phone_number_type ntype
523536)
524537{
538+ auto number = reinterpret_cast<PhoneNumber const *>(ptr);
525539 return unmarshal_validation_result(
526540 PhoneNumberUtil::GetInstance()->IsPossibleNumberForTypeWithReason(
527541 *number,
@@ -530,13 +544,15 @@ extern "C" validation_result c_phone_number_util_is_possible_number_for_type_wit
530544 );
531545}
532546
533−extern "C" int c_phone_number_util_can_be_internationally_dialed(PhoneNumber const *number)
547+extern "C" int c_phone_number_util_can_be_internationally_dialed(CxxPhoneNumber const *ptr)
534548{
549+ auto number = reinterpret_cast<PhoneNumber const *>(ptr);
535550 return PhoneNumberUtil::GetInstance()->CanBeInternationallyDialled(*number);
536551}
537552
538−extern "C" int c_phone_number_util_is_number_geographical_1(PhoneNumber const *number)
553+extern "C" int c_phone_number_util_is_number_geographical_1(CxxPhoneNumber const *ptr)
539554{
555+ auto number = reinterpret_cast<PhoneNumber const *>(ptr);
540556 return PhoneNumberUtil::GetInstance()->IsNumberGeographical(*number);
541557}
542558
@@ -556,14 +572,14 @@ extern "C" error_type c_phone_number_util_parse(
556572 size_t c_string_size,
557573 char const *c_region,
558574 size_t c_region_size,
559− PhoneNumber **number
575+ CxxPhoneNumber **number
560576)
561577{
562578 std::string const string(c_string, c_string_size);
563579 std::string const region(c_region, c_region_size);
564− *number = new PhoneNumber();
580+ *number = reinterpret_cast<CxxPhoneNumber *>(new PhoneNumber());
565581 return unmarshal_error_type(
566− PhoneNumberUtil::GetInstance()->Parse(string, region, *number)
582+ PhoneNumberUtil::GetInstance()->Parse(string, region, reinterpret_cast<PhoneNumber *>(*number))
567583 );
568584}
569585
@@ -572,22 +588,24 @@ extern "C" error_type c_phone_number_util_parse_and_keep_raw_input(
572588 size_t c_string_size,
573589 char const *c_region,
574590 size_t c_region_size,
575− PhoneNumber **number
591+ CxxPhoneNumber **number
576592)
577593{
578594 std::string const string(c_string, c_string_size);
579595 std::string const region(c_region, c_region_size);
580− *number = new PhoneNumber();
596+ *number = reinterpret_cast<CxxPhoneNumber *>(new PhoneNumber());
581597 return unmarshal_error_type(
582− PhoneNumberUtil::GetInstance()->ParseAndKeepRawInput(string, region, *number)
598+ PhoneNumberUtil::GetInstance()->ParseAndKeepRawInput(string, region, reinterpret_cast<PhoneNumber *>(*number))
583599 );
584600}
585601
586602extern "C" match_type c_phone_number_util_is_number_match(
587− PhoneNumber const *number1,
588− PhoneNumber const *number2
603+ CxxPhoneNumber const *ptr1,
604+ CxxPhoneNumber const *ptr2
589605)
590606{
607+ auto number1 = reinterpret_cast<PhoneNumber const *>(ptr1);
608+ auto number2 = reinterpret_cast<PhoneNumber const *>(ptr2);
591609 return unmarshal_match_type(
592610 PhoneNumberUtil::GetInstance()->IsNumberMatch(*number1, *number2)
593611 );
@@ -608,11 +626,12 @@ extern "C" match_type c_phone_number_util_is_number_match_with_two_strings(
608626}
609627
610628extern "C" match_type c_phone_number_util_is_number_match_with_one_string(
611− PhoneNumber const *number1,
629+ CxxPhoneNumber const *ptr1,
612630 char const *c_number2,
613631 size_t c_number2_size
614632)
615633{
634+ auto number1 = reinterpret_cast<PhoneNumber const *>(ptr1);
616635 std::string const number2(c_number2, c_number2_size);
617636 return unmarshal_match_type(
618637 PhoneNumberUtil::GetInstance()->IsNumberMatchWithOneString(*number1, number2)
include/c_phonenumber.h+4−8
@@ -43,20 +43,16 @@ typedef struct
4343 char has_number_of_leading_zeros;
4444} phone_number;
4545
46−#ifdef __cplusplus
47−typedef i18n::phonenumbers::PhoneNumber PhoneNumber;
48−#else
49−typedef struct PhoneNumber PhoneNumber;
50−#endif
46+struct CxxPhoneNumber;
5147
5248// The returned object is allocated by `new` and managed by C++ RAII, and it is
5349// haskell's responsibility to call its finalizer (c_phone_number_free).
54−PhoneNumber *c_phone_number_marshal(phone_number const *);
50+struct CxxPhoneNumber *c_phone_number_marshal(phone_number const *);
5551
5652// phone_number * points to an area which will be filled with the data of the
5753// given PhoneNumber.
58−void c_phone_number_unmarshal(PhoneNumber const *, phone_number *);
54+void c_phone_number_unmarshal(struct CxxPhoneNumber const *, phone_number *);
5955
60−void c_phone_number_free(PhoneNumber *);
56+void c_phone_number_free(struct CxxPhoneNumber *);
6157
6258#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(PhoneNumber const *, c_string *);
100+void c_phone_number_util_get_national_significant_number(struct CxxPhoneNumber const *, c_string *);
101101void c_phone_number_util_get_country_mobile_token(int, 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 *);
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 *);
109109// /!\ May return NULL
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 *);
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 *);
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(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 *);
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 *);
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, 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 *);
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 *);
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(PhoneNumber const *, 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);
129129
130130#endif
include/common.h+3−2
@@ -4,6 +4,7 @@
44#include <cstddef>
55#include <cstring>
66#include <type_traits>
7+#include <utility>
78
89// Given `enum T { A, B, C }` and `enum S { X, Y, Z }`, we can use
910// `is_same_enum<enum_values<T, A, B, C>, enum_values<S, X, Y, Z>>::value`
@@ -31,7 +32,7 @@ struct is_same_enum: std::false_type
3132
3233template<typename T, T... TVals, typename S, S... SVals>
3334struct is_same_enum<enum_values<T, TVals...>, enum_values<S, SVals...>>:
34− std::integral_constant<bool, std::is_same_v<
35+ std::integral_constant<bool, std::is_same<
3536 std::integer_sequence<
3637 typename std::common_type<
3738 typename std::underlying_type<T>::type,
@@ -46,7 +47,7 @@ struct is_same_enum<enum_values<T, TVals...>, enum_values<S, SVals...>>:
4647 >::type,
4748 SVals...
4849 >
49− >>
50+ >::value>
5051{};
5152
5253inline void unmarshal_string_size(std::string const &string, char *&c_string, size_t &c_string_size)
libphonenumber.cabal+58−40
@@ -1,6 +1,6 @@
11cabal-version: 3.0
22name: libphonenumber
3−version: 0.0.0.0
3+version: 0.1.4.0
44category: Data
55synopsis: Parsing, formatting, and validating international phone
66 numbers
@@ -8,7 +8,8 @@ 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@.
11+ and @ShortNumberInfo@. The C++ library must be of version
12+ >= 8.7.0.
1213
1314 The interface largely resembles that of the original unified
1415 C++, Java, and JavaScript version of the library, with some
@@ -24,44 +25,61 @@ description:
2425 and to avoid a dependency on @text@, we use 'ByteString'
2526 throughout the library instead of 'Data.Text.Text'.
2627
27−copyright: 2023 Typeable
28+copyright: 2023-2025 Typeable
2829author: Typeable
2930maintainer: mniip@typeable.io
3031homepage: https://github.com/typeable/libphonenumber/
3132license: MIT
3233license-file: LICENSE
3334
34−build-type: Simple
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
3549
3650extra-source-files:
37− include/*.h
38− test/libphonenumber.supp
51+ , include/*.h
52+ , test/libphonenumber.supp
53+
54+source-repository head
55+ type: git
56+ location: https://github.com/typeable/libphonenumber/
3957
4058library
4159 exposed-modules:
42− Data.PhoneNumber.Number
43− Data.PhoneNumber.Util
60+ , Data.PhoneNumber.Number
61+ , Data.PhoneNumber.Util
4462 other-modules:
45− Data.PhoneNumber.Internal.Common
46− Data.PhoneNumber.Internal.Number
47− Data.PhoneNumber.Internal.Util
63+ , Data.PhoneNumber.Internal.Common
64+ , Data.PhoneNumber.Internal.Number
65+ , Data.PhoneNumber.Internal.Util
4866 build-depends:
49− base >= 4.12 && < 4.18
50− , bytestring
51− , containers
52− , deepseq
53− , transformers
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
5472 build-tool-depends: c2hs:c2hs
73+ pkgconfig-depends: protobuf
5574 include-dirs: include
5675 includes:
57− c_phonenumber.h
58− c_phonenumberutil.h
76+ , c_phonenumber.h
77+ , c_phonenumberutil.h
5978 cxx-sources:
60− cxxbits/c_phonenumber.cpp
61− cxxbits/c_phonenumberutil.cpp
79+ , cxxbits/c_phonenumber.cpp
80+ , cxxbits/c_phonenumberutil.cpp
6281 extra-libraries:
63− phonenumber
64− protobuf
82+ , phonenumber
6583 if impl(ghc >= 9.4)
6684 build-depends: system-cxx-std-lib
6785 else
@@ -69,31 +87,31 @@ library
6987 hs-source-dirs: src
7088 default-language: Haskell2010
7189 default-extensions:
72− DeriveAnyClass
73− DeriveDataTypeable
74− DeriveGeneric
75− DerivingStrategies
76− GeneralizedNewtypeDeriving
77− LambdaCase
78− NamedFieldPuns
79− OverloadedStrings
80− PatternSynonyms
81− StandaloneDeriving
82− RecordWildCards
83− ViewPatterns
90+ , DeriveAnyClass
91+ , DeriveDataTypeable
92+ , DeriveGeneric
93+ , DerivingStrategies
94+ , GeneralizedNewtypeDeriving
95+ , LambdaCase
96+ , NamedFieldPuns
97+ , OverloadedStrings
98+ , PatternSynonyms
99+ , StandaloneDeriving
100+ , RecordWildCards
101+ , ViewPatterns
84102 if impl(ghc >= 9.2)
85103 default-extensions: NoFieldSelectors
86− ghc-options: -Wall -Wextra -Werror
104+ ghc-options: -Wall -Wextra
87105
88106test-suite spec_test
89107 type: exitcode-stdio-1.0
90108 build-depends:
91− base
109+ , base
92110 , bytestring
93111 , containers
94− , hspec
112+ , hspec >= 2.10.8 && < 2.12
95113 , libphonenumber
96− , QuickCheck
114+ , QuickCheck >= 2.13.1 && < 2.16
97115 main-is: Spec.hs
98116 hs-source-dirs: test
99117 default-language: Haskell2010
@@ -102,9 +120,9 @@ test-suite spec_test
102120test-suite leak_test
103121 type: exitcode-stdio-1.0
104122 build-depends:
105− base
123+ , base
106124 , libphonenumber
107− , process
125+ , process >= 1.6.5 && < 1.7
108126 main-is: Leak.hs
109127 hs-source-dirs: test
110128 default-language: Haskell2010
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 *PhoneNumber foreign finalizer c_phone_number_free newtype#}
129+{#pointer *CxxPhoneNumber as PhoneNumber foreign finalizer c_phone_number_free newtype#}
130130
131131{#fun unsafe c_phone_number_marshal
132132 { withCPhoneNumber* `CPhoneNumber'
src/Data/PhoneNumber/Number.hs+69−10
@@ -21,16 +21,10 @@ module Data.PhoneNumber.Number
2121 ) where
2222
2323import Data.ByteString (ByteString)
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 )
24+import Data.PhoneNumber.Internal.Number
25+ ( CPhoneNumber(CPhoneNumber), PhoneNumber, CountryCodeSource, toCPhoneNumber
26+ , fromCPhoneNumber
27+ )
3428import qualified Data.PhoneNumber.Internal.Number as I
3529
3630-- | A country calling code (International Subscriber Dialing code, ISD code),
@@ -79,3 +73,68 @@ pattern PhoneNumber
7973 = fromCPhoneNumber $ CPhoneNumber{..}
8074
8175{-# 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+-- @
83+extension :: PhoneNumber -> Maybe ByteString
84+
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+-- @
91+rawInput :: PhoneNumber -> Maybe ByteString
92+
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+-- @
99+preferredDomesticCarrierCode :: PhoneNumber -> Maybe ByteString
100+
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+-- @
108+nationalNumber :: PhoneNumber -> Word
109+
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+-- @
116+countryCode :: PhoneNumber -> CountryCode
117+
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+-- @
124+italianLeadingZero :: PhoneNumber -> Maybe Bool
125+
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+-- @
132+countryCodeSource :: PhoneNumber -> Maybe CountryCodeSource
133+
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+-- @
140+numberOfLeadingZeros :: PhoneNumber -> Maybe Int
src/Data/PhoneNumber/Util.hs+6−6
@@ -371,12 +371,12 @@ nddPrefixForRegion strip (Region region) = unsafeDupablePerformIO $ mask_ $
371371-- 3. For some numbers (particularly fixed-line), many regions have the concept
372372-- of area code, which together with subscriber number constitute the National
373373-- Significant Number. It is sometimes okay to dial only the subscriber number
374−-- when dialing in the same area. This function will return IsPossibleLocalOnly
375−-- if the subscriber-number-only version is passed in. On the other hand,
376−-- because 'isValidNumber' validates using information on both starting digits
377−-- (for fixed line numbers, that would most likely be area codes) and length
378−-- (obviously includes the length of area codes for fixed line numbers), it will
379−-- return 'False' for the subscriber-number-only version.
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.
380380possibleNumber :: PhoneNumberType -> PhoneNumber -> ValidationResult
381381possibleNumber ntype pn = unsafeDupablePerformIO $
382382 c_phone_number_util_is_possible_number_for_type_with_reason pn ntype
test/Spec.hs+34−27
@@ -53,11 +53,13 @@ data ParsedNumber = ParsedNumber (ParseMode, Maybe Region, ParserInput) PhoneNum
5353 deriving stock (Show)
5454
5555instance Arbitrary ParsedNumber where
56− arbitrary = liftA3 (,,) (elements [Canonicalize, KeepRawInput]) arbitrary arbitrary
57− `suchThatMap` \(mode, MReg mRegion, ParserInput input) ->
58− case parseNumber mode mRegion input of
59− Right pn -> Just $ ParsedNumber (mode, mRegion, ParserInput input) pn
60− _ -> Nothing
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
6163 shrink (ParsedNumber (mode, region, input) _) = do
6264 ParserInput input' <- shrink input
6365 case parseNumber mode region input' of
@@ -113,7 +115,7 @@ main = hspec $ modifyMaxSuccess (* 10) $ do
113115 .&&. not (BS.null $ normalizeNumber Digits $ BS.pack str <> "0")
114116 describe "Dialable" $ do
115117 -- PhoneNumberUtil::NormalizeDiallableCharsOnly will error on any invalid UTF8
116− xprop "doesn't randomly fail" $ \str ->
118+ prop "doesn't randomly fail" $ \str ->
117119 not (BS.null $ normalizeNumber Digits $ "0" <> BS.pack str)
118120 .&&. not (BS.null $ normalizeNumber Digits $ BS.pack str <> "0")
119121
@@ -123,14 +125,9 @@ main = hspec $ modifyMaxSuccess (* 10) $ do
123125 prop "returns only ASCII digits" $ \(ParsedNumber _ pn) ->
124126 BSC.all (`elem` ['0'..'9']) $ nationalSignificantNumber pn
125127 modifyMaxDiscardRatio (* 10) $ do
126− prop "concatenated with country code parses to the original (if valid and not MX)" $
127− \(ParsedNumber (_, _, ParserInput input) pn) ->
128− -- Apparently "+52112210000000" makes the parser/formatter do weird
129− -- stuff as it tries to handle the deprecated (?) mobile token ("1"
130− -- after "+52")
131− if "5211" `BS.isPrefixOf` normalizeNumber Digits input
132− then discard
133− else isValidNumber Nothing pn ==>
128+ prop "concatenated with country code parses to the original (if valid)" $
129+ \(ParsedNumber _ pn) ->
130+ isValidNumber Nothing pn ==>
134131 let
135132 cc = countryCode pn
136133 nsn = nationalSignificantNumber pn
@@ -145,18 +142,28 @@ main = hspec $ modifyMaxSuccess (* 10) $ do
145142 BSC.all (`elem` ['0'..'9']) $ countryMobileToken cc
146143
147144 describe "formatNumber" $ do
148− prop "with International produces a parseable result" $
149− \(ParsedNumber _ pn) -> possibleNumber Unknown pn == IsPossible ==>
150− isRight $ parseNumber Canonicalize Nothing
151− $ formatNumber International pn
152− prop "with E164 produces a parseable result" $
145+ prop "with International produces a parseable result (if no ext)" $
153146 \(ParsedNumber _ pn) -> possibleNumber Unknown pn == IsPossible ==>
154− isRight $ parseNumber Canonicalize Nothing
155− $ formatNumber E164 pn
156− prop "with RFC3966 produces a parseable result" $
157− \(ParsedNumber _ pn) -> possibleNumber Unknown pn == IsPossible ==>
158− isRight $ parseNumber Canonicalize Nothing
159− $ formatNumber RFC3966 pn
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
160167
161168 describe "truncateTooLongNumber" $ do
162169 pure ()
@@ -219,8 +226,8 @@ main = hspec $ modifyMaxSuccess (* 10) $ do
219226 describe "possibleNumber" $ do
220227 modifyMaxDiscardRatio (* 10) $ do
221228 prop "with Unknown is more lenient than isValidNumber" $
222− \(ParsedNumber _ pn) ->
223− isValidNumber Nothing pn ==> possibleNumber Unknown pn === IsPossible
229+ \(ParsedNumber _ pn) -> isValidNumber Nothing pn ==>
230+ possibleNumber Unknown pn `elem` [IsPossible, IsPossibleLocalOnly]
224231
225232 describe "canBeInternationallyDialed" $ do
226233 pure ()