pico_phone 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b484e1d4028dcaf1cd1a8ef45092853a111df96ce88c2b23a1a5d0adb95292bd
4
- data.tar.gz: acd5433d7b8c973c10d1c01560e4c4367c4d5a2828178de8cd9970b4d8fc32cf
3
+ metadata.gz: bac62c280ce5ac633b95536c04c0bc7bb0672beaa0a44928ba128b367c845b93
4
+ data.tar.gz: b567f397b99d3a93b9309c19c6797eb6b96a4db3e7c3860d2c5e9d0a9033fc41
5
5
  SHA512:
6
- metadata.gz: f4612e8029c13f8498e4f78a15913641ecb46e6f2e2864844209a5e6f962cad7168f83aaa412fc948e6520ff6709bb45eb9f9fd30ac275c37d6ee5596947b1f5
7
- data.tar.gz: c255297aba501623563ca3132b30f6ae7fec878de5f41a8938b9b86bd11f5780d4258f5e095c2c24695bbc02b9ffab796bf0ce0dd6b4d428367d157bf0b38adc
6
+ metadata.gz: 17a4f165dc820b929d587401ab31c895701d74d4802cfb414e1c044345e7e733b787a7956e02e5dd55228ab83f9bbf917c575b0550f62597120577c5bcc2df05
7
+ data.tar.gz: 4aa9e0e6507d71f14fcaf11242d2a0b666707eebb60afffbf85988414a771bc6596d13638cbe8e9ea5ffb66e0bd08720d1064e3fa799683eb87c1d34340f3724
@@ -14,13 +14,18 @@
14
14
  // any ICU header is reachable.
15
15
  #undef UChar
16
16
  #include <phonenumbers/geocoding/phonenumber_offline_geocoder.h>
17
+ #include <phonenumbers/phonenumbermatcher.h>
18
+ #include <phonenumbers/phonenumbermatch.h>
17
19
  #include "carrier_mapper.h"
18
20
  #include "timezone_mapper.h"
19
21
 
22
+ #include <climits>
23
+
20
24
  using namespace Rice;
21
25
  using namespace i18n::phonenumbers;
22
26
 
23
27
  static VALUE rb_cPhoneNumber;
28
+ static VALUE rb_cPhoneNumberMatch;
24
29
  static VALUE rb_mPicoPhone;
25
30
 
26
31
  size_t phone_number_size(const void *data) { return sizeof(PhoneNumber); }
@@ -195,6 +200,112 @@ Object pico_phone_is_alpha_number(Object self, String str) {
195
200
  return phone_util.IsAlphaNumber(str.c_str()) ? Qtrue : Qfalse;
196
201
  }
197
202
 
203
+ int pico_phone_country_calling_code(Object self, String region) {
204
+ const PhoneNumberUtil &phone_util(*PhoneNumberUtil::GetInstance());
205
+ return phone_util.GetCountryCodeForRegion(std::string(region.c_str()));
206
+ }
207
+
208
+ static VALUE match_type_to_symbol(PhoneNumberUtil::MatchType match_type) {
209
+ switch (match_type) {
210
+ case PhoneNumberUtil::INVALID_NUMBER: return rb_id2sym(rb_intern("invalid_number"));
211
+ case PhoneNumberUtil::NO_MATCH: return rb_id2sym(rb_intern("no_match"));
212
+ case PhoneNumberUtil::SHORT_NSN_MATCH: return rb_id2sym(rb_intern("short_nsn_match"));
213
+ case PhoneNumberUtil::NSN_MATCH: return rb_id2sym(rb_intern("nsn_match"));
214
+ case PhoneNumberUtil::EXACT_MATCH: return rb_id2sym(rb_intern("exact_match"));
215
+ default: return rb_id2sym(rb_intern("no_match"));
216
+ }
217
+ }
218
+
219
+ Object pico_phone_number_match(Object self, String first, String second) {
220
+ const PhoneNumberUtil &phone_util(*PhoneNumberUtil::GetInstance());
221
+ return match_type_to_symbol(phone_util.IsNumberMatchWithTwoStrings(first.c_str(), second.c_str()));
222
+ }
223
+
224
+ Object parsed_number_match_type(Object self, Object other) {
225
+ PhoneNumber *phone_number;
226
+ TypedData_Get_Struct(self, PhoneNumber, &phone_number_type, phone_number);
227
+ const PhoneNumberUtil &phone_util(*PhoneNumberUtil::GetInstance());
228
+
229
+ if (RTEST(rb_obj_is_kind_of(other, rb_cPhoneNumber))) {
230
+ PhoneNumber *other_number;
231
+ TypedData_Get_Struct(other, PhoneNumber, &phone_number_type, other_number);
232
+ return match_type_to_symbol(phone_util.IsNumberMatch(*phone_number, *other_number));
233
+ }
234
+
235
+ VALUE other_val = other.value();
236
+ std::string other_str(StringValuePtr(other_val), RSTRING_LEN(other_val));
237
+ return match_type_to_symbol(phone_util.IsNumberMatchWithOneString(*phone_number, other_str));
238
+ }
239
+
240
+ static PhoneNumberMatcher::Leniency symbol_to_leniency(VALUE sym) {
241
+ ID id = rb_to_id(sym);
242
+ if (id == rb_intern("possible")) return PhoneNumberMatcher::POSSIBLE;
243
+ if (id == rb_intern("strict_grouping")) return PhoneNumberMatcher::STRICT_GROUPING;
244
+ if (id == rb_intern("exact_grouping")) return PhoneNumberMatcher::EXACT_GROUPING;
245
+ return PhoneNumberMatcher::VALID;
246
+ }
247
+
248
+ Object phone_number_match_start(Object self) {
249
+ return rb_ivar_get(self, rb_intern("@start"));
250
+ }
251
+
252
+ Object phone_number_match_end_index(Object self) {
253
+ return rb_ivar_get(self, rb_intern("@end_index"));
254
+ }
255
+
256
+ Object phone_number_match_raw_string(Object self) {
257
+ return rb_ivar_get(self, rb_intern("@raw_string"));
258
+ }
259
+
260
+ Object phone_number_match_number(Object self) {
261
+ return rb_ivar_get(self, rb_intern("@number"));
262
+ }
263
+
264
+ VALUE pico_phone_find_numbers(int argc, VALUE *argv, VALUE self) {
265
+ VALUE text, region, kwargs;
266
+ rb_scan_args(argc, argv, "2:", &text, &region, &kwargs);
267
+
268
+ ID kwarg_ids[1] = { rb_intern("leniency") };
269
+ VALUE kwarg_vals[1];
270
+ rb_get_kwargs(kwargs, kwarg_ids, 0, 1, kwarg_vals);
271
+
272
+ PhoneNumberMatcher::Leniency leniency = PhoneNumberMatcher::VALID;
273
+ if (kwarg_vals[0] != Qundef) {
274
+ leniency = symbol_to_leniency(kwarg_vals[0]);
275
+ }
276
+
277
+ std::string text_str(StringValuePtr(text), RSTRING_LEN(text));
278
+ std::string region_str(StringValuePtr(region), RSTRING_LEN(region));
279
+
280
+ const PhoneNumberUtil &phone_util(*PhoneNumberUtil::GetInstance());
281
+ PhoneNumberMatcher matcher(phone_util, text_str, region_str, leniency, INT_MAX);
282
+
283
+ Array result;
284
+ PhoneNumberMatch match;
285
+ while (matcher.HasNext()) {
286
+ matcher.Next(&match);
287
+
288
+ int start_pos = match.start();
289
+ int end_pos = match.end();
290
+ std::string raw = match.raw_string();
291
+
292
+ std::string e164;
293
+ phone_util.Format(match.number(), PhoneNumberUtil::E164, &e164);
294
+ VALUE num_args[1] = { rb_str_new(e164.c_str(), e164.size()) };
295
+ VALUE number_obj = rb_class_new_instance(1, num_args, rb_cPhoneNumber);
296
+
297
+ VALUE match_obj = rb_obj_alloc(rb_cPhoneNumberMatch);
298
+ rb_ivar_set(match_obj, rb_intern("@start"), INT2FIX(start_pos));
299
+ rb_ivar_set(match_obj, rb_intern("@end_index"), INT2FIX(end_pos));
300
+ rb_ivar_set(match_obj, rb_intern("@raw_string"), rb_str_new(raw.c_str(), raw.size()));
301
+ rb_ivar_set(match_obj, rb_intern("@number"), number_obj);
302
+
303
+ result.push(Object(match_obj));
304
+ }
305
+
306
+ return result;
307
+ }
308
+
198
309
  static const ShortNumberInfo& GetShortNumberInfo() {
199
310
  static ShortNumberInfo instance;
200
311
  return instance;
@@ -850,6 +961,24 @@ Object parsed_number_possible_for_type(Object self, Object type_sym) {
850
961
  return phone_util.IsPossibleNumberForType(*phone_number, type) ? Qtrue : Qfalse;
851
962
  }
852
963
 
964
+ Object parsed_number_truncate(Object self) {
965
+ PhoneNumber *phone_number;
966
+ TypedData_Get_Struct(self, PhoneNumber, &phone_number_type, phone_number);
967
+ const PhoneNumberUtil &phone_util(*PhoneNumberUtil::GetInstance());
968
+
969
+ // TruncateTooLongNumber returns true for already-valid numbers without
970
+ // modifying them. Guard so callers get nil for the "nothing to do" case.
971
+ if (phone_util.IsValidNumber(*phone_number)) return Qnil;
972
+
973
+ PhoneNumber copy(*phone_number);
974
+ if (!phone_util.TruncateTooLongNumber(&copy)) return Qnil;
975
+
976
+ std::string formatted;
977
+ phone_util.Format(copy, PhoneNumberUtil::E164, &formatted);
978
+ VALUE args[1] = { rb_str_new(formatted.c_str(), formatted.size()) };
979
+ return rb_class_new_instance(1, args, rb_cPhoneNumber);
980
+ }
981
+
853
982
  extern "C"
854
983
  void Init_pico_phone() {
855
984
  rb_mPicoPhone = define_module("PicoPhone")
@@ -863,12 +992,16 @@ void Init_pico_phone() {
863
992
  .define_singleton_method("supported_regions", &pico_phone_supported_regions)
864
993
  .define_singleton_method("convert_alpha_characters", &pico_phone_convert_alpha_characters)
865
994
  .define_singleton_method("alpha_number?", &pico_phone_is_alpha_number)
995
+ .define_singleton_method("number_match", &pico_phone_number_match)
866
996
  .define_singleton_method("emergency_number?", &pico_phone_is_emergency_number)
867
997
  .define_singleton_method("short_number_valid?", &pico_phone_is_short_number_valid)
868
998
  .define_singleton_method("short_number_cost", &pico_phone_short_number_cost)
869
999
  .define_singleton_method("supported_types_for_region", &pico_phone_supported_types_for_region)
870
1000
  .define_singleton_method("example_number", &pico_phone_example_number)
871
- .define_singleton_method("example_number_for_type", &pico_phone_example_number_for_type);
1001
+ .define_singleton_method("example_number_for_type", &pico_phone_example_number_for_type)
1002
+ .define_singleton_method("country_calling_code", &pico_phone_country_calling_code);
1003
+
1004
+ rb_define_singleton_method(rb_mPicoPhone, "find_numbers", reinterpret_cast<VALUE (*)(...)>(pico_phone_find_numbers), -1);
872
1005
 
873
1006
  rb_define_module_function(rb_mPicoPhone, "default_country=", reinterpret_cast<VALUE (*)(...)>(pico_phone_set_default_country), 1);
874
1007
  rb_define_module_function(rb_mPicoPhone, "default_extension_prefix=", reinterpret_cast<VALUE (*)(...)>(pico_phone_set_default_extension_prefix), 1);
@@ -909,7 +1042,15 @@ void Init_pico_phone() {
909
1042
  .define_method("geographical?", &parsed_number_geographical)
910
1043
  .define_method("can_be_internationally_dialled?", &parsed_number_can_be_internationally_dialled)
911
1044
  .define_method("possible_for_type?", &parsed_number_possible_for_type)
912
- .define_method("timezones", &parsed_number_timezones);
1045
+ .define_method("timezones", &parsed_number_timezones)
1046
+ .define_method("truncate", &parsed_number_truncate)
1047
+ .define_method("match_type", &parsed_number_match_type);
1048
+
1049
+ rb_cPhoneNumberMatch = define_class_under(rb_mPicoPhone, "PhoneNumberMatch")
1050
+ .define_method("start", &phone_number_match_start)
1051
+ .define_method("end_index", &phone_number_match_end_index)
1052
+ .define_method("raw_string", &phone_number_match_raw_string)
1053
+ .define_method("number", &phone_number_match_number);
913
1054
 
914
1055
  rb_define_alloc_func(rb_cPhoneNumber, rb_phone_number_alloc);
915
1056
  rb_define_method(rb_cPhoneNumber, "initialize", reinterpret_cast<VALUE (*)(...)>(phone_number_initialize), -1);
@@ -79,6 +79,46 @@ module PicoPhone
79
79
  # @param string [String]
80
80
  # @return [Boolean]
81
81
 
82
+ # @!method self.country_calling_code(region)
83
+ # Returns the international calling code for a region.
84
+ # Returns 0 for unknown or invalid region codes.
85
+ # @param region [String] ISO 3166-1 alpha-2 region code (e.g. "US")
86
+ # @return [Integer] e.g. 1 for "US", 33 for "FR", 0 for unknown
87
+
88
+ # @!method self.number_match(first, second)
89
+ # Compare two phone number strings and return how closely they match.
90
+ # Neither string requires a region hint; E.164 input gives the most precise result.
91
+ # @param first [String]
92
+ # @param second [String]
93
+ # @return [Symbol] :exact_match, :nsn_match, :short_nsn_match, :no_match, or :invalid_number
94
+
95
+ # @!method self.find_numbers(text, region, leniency: :valid)
96
+ # Scan a block of text and return every phone number found in it.
97
+ # @param text [String] arbitrary text to search (must be valid UTF-8)
98
+ # @param region [String] ISO 3166-1 alpha-2 default region for numbers without a country code
99
+ # @param leniency [Symbol] :possible, :valid (default), :strict_grouping, or :exact_grouping
100
+ # @return [Array<PhoneNumberMatch>]
101
+
102
+ # Represents a single phone number match found by {PicoPhone.find_numbers}.
103
+ class PhoneNumberMatch
104
+ # Byte offset of the match start within the searched text.
105
+ # @return [Integer]
106
+ def start; end
107
+
108
+ # Exclusive byte offset of the match end within the searched text.
109
+ # The matched substring is text[start...end_index].
110
+ # @return [Integer]
111
+ def end_index; end
112
+
113
+ # The substring of the searched text that was matched.
114
+ # @return [String]
115
+ def raw_string; end
116
+
117
+ # The parsed phone number.
118
+ # @return [PhoneNumber]
119
+ def number; end
120
+ end
121
+
82
122
  class PhoneNumber
83
123
  # @param string [String, nil] raw phone number input
84
124
  # @param region [String, nil] ISO 3166-1 alpha-2 region hint (e.g. "US")
@@ -226,6 +266,14 @@ module PicoPhone
226
266
  # @return [Boolean]
227
267
  def geographical?; end
228
268
 
269
+ # Compare this number against another and return how closely they match.
270
+ # Pass a String or a PhoneNumber. When passed a PhoneNumber, the country code
271
+ # stored in the proto is used for comparison, giving more precise results than
272
+ # a bare national-format string.
273
+ # @param other [String, PhoneNumber]
274
+ # @return [Symbol] :exact_match, :nsn_match, :short_nsn_match, :no_match, or :invalid_number
275
+ def match_type(other); end
276
+
229
277
  # True when the number's digit count is consistent with the given type in its region.
230
278
  # More permissive than {#type}: a 10-digit US number is possible for both
231
279
  # :fixed_line_or_mobile and :toll_free since they share the same digit count.
@@ -236,5 +284,11 @@ module PicoPhone
236
284
  # False for numbers that only work within their own country.
237
285
  # @return [Boolean]
238
286
  def can_be_internationally_dialled?; end
287
+
288
+ # Removes trailing digits until the number is valid, returning the truncated
289
+ # number as a new PhoneNumber. Returns nil if the number was not too long or
290
+ # if no valid truncation exists. Does not mutate the receiver.
291
+ # @return [PhoneNumber, nil]
292
+ def truncate; end
239
293
  end
240
294
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PicoPhone
4
- VERSION = "0.5.0"
4
+ VERSION = "0.6.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pico_phone
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gabi Jack
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-07-17 00:00:00.000000000 Z
11
+ date: 2026-07-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rice