finish helper functions
This commit is contained in:
544
Cargo.lock
generated
544
Cargo.lock
generated
@@ -2,6 +2,21 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "0.1.8"
|
||||
@@ -33,33 +48,138 @@ version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
|
||||
[[package]]
|
||||
name = "cloudabi"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "6.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"hashbrown 0.14.5",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fast-cat"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42499c1ad661a77bb65d72a294f1ed9651d374035713cb22b92fc581ced91cd8"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-cprng"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasi 0.14.2+wasi-0.2.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.10.1"
|
||||
@@ -96,6 +216,16 @@ dependencies = [
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.15.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iron"
|
||||
version = "0.6.1"
|
||||
@@ -112,6 +242,12 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "language-tags"
|
||||
version = "0.2.2"
|
||||
@@ -124,6 +260,28 @@ version = "0.2.174"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
|
||||
dependencies = [
|
||||
"autocfg 1.5.0",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.3.9"
|
||||
@@ -156,6 +314,12 @@ version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.2.6"
|
||||
@@ -199,6 +363,19 @@ version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "1.0.1"
|
||||
@@ -270,7 +447,38 @@ checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"protobuf-support",
|
||||
"thiserror",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf-codegen"
|
||||
version = "3.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d3976825c0014bbd2f3b34f0001876604fe87e0c86cd8fa54251530f1544ace"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"once_cell",
|
||||
"protobuf",
|
||||
"protobuf-parse",
|
||||
"regex",
|
||||
"tempfile",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf-parse"
|
||||
version = "3.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4aeaa1f2460f1d348eeaeed86aea999ce98c1bded6f089ff8514c9d9dbdc973"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
"log 0.4.27",
|
||||
"protobuf",
|
||||
"protobuf-support",
|
||||
"tempfile",
|
||||
"thiserror 1.0.69",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -279,7 +487,7 @@ version = "3.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -291,6 +499,12 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.6.5"
|
||||
@@ -406,26 +620,137 @@ dependencies = [
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "rlibphonenumbers"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"dashmap",
|
||||
"fast-cat",
|
||||
"itoa",
|
||||
"logger",
|
||||
"protobuf",
|
||||
"protobuf-codegen",
|
||||
"regex",
|
||||
"strum",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.4.15",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.9.4",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
||||
|
||||
[[package]]
|
||||
name = "safemem"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.104"
|
||||
@@ -437,13 +762,35 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix 1.0.7",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
"thiserror-impl 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -457,6 +804,17 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.45"
|
||||
@@ -464,7 +822,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@@ -566,6 +924,27 @@ version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.14.2+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
|
||||
dependencies = [
|
||||
"either",
|
||||
"home",
|
||||
"once_cell",
|
||||
"rustix 0.38.44",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@@ -587,3 +966,158 @@ name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm 0.52.6",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.53.0",
|
||||
"windows_aarch64_msvc 0.53.0",
|
||||
"windows_i686_gnu 0.53.0",
|
||||
"windows_i686_gnullvm 0.53.0",
|
||||
"windows_i686_msvc 0.53.0",
|
||||
"windows_x86_64_gnu 0.53.0",
|
||||
"windows_x86_64_gnullvm 0.53.0",
|
||||
"windows_x86_64_msvc 0.53.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
]
|
||||
|
||||
20
Cargo.toml
20
Cargo.toml
@@ -3,6 +3,26 @@ name = "rlibphonenumbers"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
build = "build/rust_build.rs"
|
||||
|
||||
[dependencies]
|
||||
# logging standard in rust
|
||||
logger = "0.4.0"
|
||||
# helpful error package
|
||||
thiserror = "2.0.12"
|
||||
# google protobuf lib required to use .proto files from assets
|
||||
protobuf = "3.7.2"
|
||||
# optimized concurrent map
|
||||
dashmap = "6.1.0"
|
||||
# just regex package
|
||||
regex = "1.11.1"
|
||||
# for fast 0-alloc int to string conversion
|
||||
itoa = "1.0.15"
|
||||
# simple macro for single allocation
|
||||
# concatenation of strings
|
||||
fast-cat = "0.1.1"
|
||||
strum = { version = "0.27.1", features = ["derive"] }
|
||||
|
||||
[build-dependencies]
|
||||
thiserror = "2.0.12"
|
||||
protobuf-codegen = "3.7.2"
|
||||
|
||||
15
build/file_header.txt
Normal file
15
build/file_header.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright (C) 2025 @Vloldik
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// This file is generated automatically, do not edit it manually.
|
||||
81
build/rust_build.rs
Normal file
81
build/rust_build.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* This file represents content of https://github.com/google/libphonenumber/tree/master/tools/cpp
|
||||
*/
|
||||
|
||||
use std::{collections::BTreeMap, fs::File, io::{BufRead, BufReader}, num::ParseIntError, path::Path};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
enum BuildError {
|
||||
#[error("IO error occurred: {0}")]
|
||||
IO(#[from] std::io::Error),
|
||||
|
||||
#[error("Line {line_num} is too long (max is {max_len} bytes)")]
|
||||
LineTooLong { line_num: usize, max_len: usize },
|
||||
|
||||
#[error("Failed to parse prefix '{prefix}': {source}")]
|
||||
PrefixParseError {
|
||||
prefix: String,
|
||||
#[source]
|
||||
source: ParseIntError,
|
||||
},
|
||||
}
|
||||
|
||||
fn parse_prefixes(path: &str, prefixes: &mut BTreeMap<i32, String>) -> Result<(), BuildError> {
|
||||
prefixes.clear();
|
||||
|
||||
let input = File::open(path)?;
|
||||
const MAX_LINE_LENGTH: usize = 2 * 1024;
|
||||
|
||||
let mut reader = BufReader::new(input);
|
||||
let mut line_buffer = String::with_capacity(MAX_LINE_LENGTH);
|
||||
let mut line_number = 0;
|
||||
|
||||
loop {
|
||||
line_number += 1;
|
||||
line_buffer.clear();
|
||||
|
||||
let bytes_read = reader.read_line(&mut line_buffer)?;
|
||||
if bytes_read == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
if !line_buffer.ends_with('\n') {
|
||||
return Err(BuildError::LineTooLong {
|
||||
line_num: line_number,
|
||||
max_len: MAX_LINE_LENGTH,
|
||||
});
|
||||
}
|
||||
|
||||
let line = line_buffer.trim();
|
||||
if line.is_empty() || line.starts_with('#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some((prefix_str, desc)) = line.split_once('|') {
|
||||
if prefix_str.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let prefix_code = prefix_str.parse().map_err(|e| BuildError::PrefixParseError {
|
||||
prefix: prefix_str.to_string(),
|
||||
source: e,
|
||||
})?;
|
||||
prefixes.insert(prefix_code, desc.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn main() -> Result<(), BuildError> {
|
||||
protobuf_codegen::Codegen::new()
|
||||
.includes(["resources"])
|
||||
.input("resources/phonemetadata.proto")
|
||||
.input("resources/phonenumber.proto")
|
||||
.cargo_out_dir("proto_gen")
|
||||
.run_from_script();
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
|
||||
use crate::proto_gen::phonemetadata::PhoneNumberDesc;
|
||||
/// Internal phonenumber matching API used to isolate the underlying
|
||||
/// implementation of the matcher and allow different implementations to be
|
||||
/// swapped in easily.
|
||||
|
||||
pub(crate) trait MatcherApi {
|
||||
/// Returns whether the given national number (a string containing only decimal
|
||||
/// digits) matches the national number pattern defined in the given
|
||||
/// PhoneNumberDesc message.
|
||||
fn match_national_number(number: &str, number_desc: &PhoneNumberDesc, allow_prefix_match: bool) -> bool;
|
||||
fn match_national_number(&self, number: &str, number_desc: &PhoneNumberDesc, allow_prefix_match: bool) -> bool;
|
||||
}
|
||||
@@ -1,2 +1,6 @@
|
||||
mod shortnumberinfo;
|
||||
mod interfaces;
|
||||
/// This module is automatically generated from /resources/*.proto
|
||||
mod proto_gen;
|
||||
mod phonenumberutil;
|
||||
mod regexp_cache;
|
||||
|
||||
103
src/phonenumberutil/enums.rs
Normal file
103
src/phonenumberutil/enums.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
use strum::EnumIter;
|
||||
use thiserror::Error;
|
||||
|
||||
/// INTERNATIONAL and NATIONAL formats are consistent with the definition
|
||||
/// in ITU-T Recommendation E.123. However we follow local conventions such as
|
||||
/// using '-' instead of whitespace as separators. For example, the number of
|
||||
/// the Google Switzerland office will be written as "+41 44 668 1800" in
|
||||
/// INTERNATIONAL format, and as "044 668 1800" in NATIONAL format. E164
|
||||
/// format is as per INTERNATIONAL format but with no formatting applied e.g.
|
||||
/// "+41446681800". RFC3966 is as per INTERNATIONAL format, but with all spaces
|
||||
/// and other separating symbols replaced with a hyphen, and with any phone
|
||||
/// number extension appended with ";ext=". It also will have a prefix of
|
||||
/// "tel:" added, e.g. "tel:+41-44-668-1800".
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum PhoneNumberFormat {
|
||||
E164,
|
||||
International,
|
||||
National,
|
||||
RFC3966,
|
||||
}
|
||||
|
||||
/// Type of phone numbers.
|
||||
#[derive(Debug, EnumIter, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum PhoneNumberType {
|
||||
FixedLine,
|
||||
Mobile,
|
||||
/// In some regions (e.g. the USA), it is impossible to distinguish between
|
||||
/// fixed-line and mobile numbers by looking at the phone number itself.
|
||||
FixedLineOrMobile,
|
||||
/// Freephone lines
|
||||
TollFree,
|
||||
PremiumRate,
|
||||
/// The cost of this call is shared between the caller and the recipient, and
|
||||
/// is hence typically less than PREMIUM_RATE calls. See
|
||||
/// http://en.wikipedia.org/wiki/Shared_Cost_Service for more information.
|
||||
SharedCost,
|
||||
/// Voice over IP numbers. This includes TSoIP (Telephony Service over IP).
|
||||
VoIP,
|
||||
/// A personal number is associated with a particular person, and may be
|
||||
/// routed to either a MOBILE or FIXED_LINE number. Some more information can
|
||||
/// be found here: http://en.wikipedia.org/wiki/Personal_Numbers
|
||||
PersonalNumber,
|
||||
Pager,
|
||||
/// Used for "Universal Access Numbers" or "Company Numbers". They may be
|
||||
/// further routed to specific offices, but allow one number to be used for a
|
||||
/// company.
|
||||
UAN,
|
||||
/// Used for "Voice Mail Access Numbers".
|
||||
VoiceMail,
|
||||
/// A phone number is of type UNKNOWN when it does not fit any of the known
|
||||
/// patterns for a specific region.
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum MatchType {
|
||||
InvalidNumber, // NOT_A_NUMBER in the java version.
|
||||
NoMatch,
|
||||
ShortNsnMatch,
|
||||
NsnMatch,
|
||||
ExactMatch,
|
||||
}
|
||||
|
||||
// Separated enum ValidationResult into ValidationResult err and
|
||||
// ValidationResultOk for using Result<Ok, Err>
|
||||
|
||||
/// Possible outcomes when testing if a PhoneNumber is possible.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Error)]
|
||||
pub enum ValidationResultErr {
|
||||
/// The number has an invalid country calling code.
|
||||
#[error("The number has an invalid country calling code")]
|
||||
InvalidCountryCode,
|
||||
/// The number is shorter than all valid numbers for this region.
|
||||
#[error("The number is shorter than all valid numbers for this region")]
|
||||
TooShort,
|
||||
/// The number is longer than the shortest valid numbers for this region,
|
||||
/// shorter than the longest valid numbers for this region, and does not
|
||||
/// itself have a number length that matches valid numbers for this region.
|
||||
/// This can also be returned in the case where
|
||||
/// IsPossibleNumberForTypeWithReason was called, and there are no numbers of
|
||||
/// this type at all for this region.
|
||||
#[error("\
|
||||
The number is longer than the shortest valid numbers for this region,\
|
||||
shorter than the longest valid numbers for this region, and does not\
|
||||
itself have a number length that matches valid numbers for this region\
|
||||
")]
|
||||
InvalidLength,
|
||||
/// The number is longer than all valid numbers for this region.
|
||||
#[error("The number is longer than all valid numbers for this region")]
|
||||
TooLong,
|
||||
}
|
||||
|
||||
/// Possible outcomes when testing if a PhoneNumber is possible.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum ValidNumberLenType {
|
||||
/// The number length matches that of valid numbers for this region.
|
||||
IsPossible,
|
||||
/// The number length matches that of local numbers for this region only
|
||||
/// (i.e. numbers that may be able to be dialled within an area, but do not
|
||||
/// have all the information to be dialled from anywhere inside or outside
|
||||
/// the country).
|
||||
IsPossibleLocalOnly,
|
||||
}
|
||||
60
src/phonenumberutil/helper_constants/helper_constants.rs
Normal file
60
src/phonenumberutil/helper_constants/helper_constants.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
// The minimum and maximum length of the national significant number.
|
||||
pub const MIN_LENGTH_FOR_NSN: usize = 2;
|
||||
// The ITU says the maximum length should be 15, but we have found longer
|
||||
// numbers in Germany.
|
||||
pub const MAX_LENGTH_FOR_NSN: usize = 17;
|
||||
/// The maximum length of the country calling code.
|
||||
pub const MAX_LENGTH_COUNTRY_CODE: usize = 3;
|
||||
pub const PLUS_CHARS: &'static str = "+\u{FF0B}";
|
||||
// Regular expression of acceptable punctuation found in phone numbers. This
|
||||
// excludes punctuation found as a leading character only. This consists of
|
||||
// dash characters, white space characters, full stops, slashes, square
|
||||
// brackets, parentheses and tildes. It also includes the letter 'x' as that
|
||||
// is found as a placeholder for carrier information in some phone numbers.
|
||||
// Full-width variants are also present.
|
||||
pub const VALID_PUNCTUATION: &'static str = "-x\
|
||||
\u{2010}-\u{2015}\u{2212}\u{30FC}\u{FF0D}-\u{FF0F} \u{00A0}\
|
||||
\u{00AD}\u{200B}\u{2060}\u{3000}()\u{FF08}\u{FF09}\u{FF3B}\
|
||||
\u{FF3D}.[]/~\u{2053}\u{223C}";
|
||||
|
||||
// Regular expression of characters typically used to start a second phone
|
||||
// number for the purposes of parsing. This allows us to strip off parts of
|
||||
// the number that are actually the start of another number, such as for:
|
||||
// (530) 583-6985 x302/x2303 -> the second extension here makes this actually
|
||||
// two phone numbers, (530) 583-6985 x302 and (530) 583-6985 x2303. We remove
|
||||
// the second extension so that the first number is parsed correctly. The
|
||||
// string preceding this is captured.
|
||||
// This corresponds to SECOND_NUMBER_START in the java version.
|
||||
pub const CAPTURE_UP_TO_SECOND_NUMBER_START: &'static str = r"(.*)[\\/] *x";
|
||||
|
||||
|
||||
pub const REGION_CODE_FOR_NON_GEO_ENTITY: &'static str = "0001";
|
||||
|
||||
pub const PLUS_SIGN: &'static str = "+";
|
||||
pub const STAR_SIGN: &'static str = "*";
|
||||
pub const RFC3966_EXTN_PREFIX: &'static str = ";ext=";
|
||||
pub const RFC3966_PREFIX: &'static str = "tel:";
|
||||
pub const RFC3966_PHONE_CONTEXT: &'static str = ";phone-context=";
|
||||
pub const RFC3966_ISDN_SUBADDRESS: &'static str = ";isub=";
|
||||
pub const RFC3966_VISUAL_SEPARATOR: &'static str = r"[\-\.\(\)]?";
|
||||
|
||||
pub const DIGITS: &'static str = r"\p{Nd}";
|
||||
|
||||
pub const VALID_ALPHA: &'static str = "a-z";
|
||||
pub const VALID_ALPHA_INCL_UPPERCASE: &'static str = "A-Za-z";
|
||||
|
||||
// Default extension prefix to use when formatting. This will be put in front of
|
||||
// any extension component of the number, after the main national number is
|
||||
// formatted. For example, if you wish the default extension formatting to be "
|
||||
// extn: 3456", then you should specify " extn: " here as the default extension
|
||||
// prefix. This can be overridden by region-specific preferences.
|
||||
pub const DEFAULT_EXTN_PREFIX: &'static str = " ext. ";
|
||||
|
||||
pub const POSSIBLE_SEPARATORS_BETWEEN_NUMBER_AND_EXT_LABEL: &'static str = "0001";
|
||||
|
||||
// Optional full stop (.) or colon, followed by zero or more
|
||||
// spaces/tabs/commas.
|
||||
pub const POSSIBLE_CHARS_AFTER_EXT_LABEL: &'static str = "[ \u{00A0}\\t,]*";
|
||||
pub const OPTIONAL_EXT_SUFFIX: &'static str = "[:\\.\u{FF0E}]?[ \u{00A0}\\t,-]*";
|
||||
|
||||
pub const NANPA_COUNTRY_CODE: i32 = 1;
|
||||
15528
src/phonenumberutil/helper_constants/metadata.rs
Normal file
15528
src/phonenumberutil/helper_constants/metadata.rs
Normal file
File diff suppressed because it is too large
Load Diff
5
src/phonenumberutil/helper_constants/mod.rs
Normal file
5
src/phonenumberutil/helper_constants/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
mod helper_constants;
|
||||
mod metadata;
|
||||
|
||||
pub(super) use helper_constants::{*};
|
||||
pub(super) use metadata::METADATA;
|
||||
452
src/phonenumberutil/helper_functions.rs
Normal file
452
src/phonenumberutil/helper_functions.rs
Normal file
@@ -0,0 +1,452 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use protobuf::Message;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::{
|
||||
interfaces::MatcherApi,
|
||||
proto_gen::{
|
||||
phonemetadata::{PhoneMetadata, PhoneMetadataCollection, PhoneNumberDesc},
|
||||
phonenumber::PhoneNumber,
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
PhoneNumberFormat, PhoneNumberType, ValidNumberLenType, ValidationResultErr,
|
||||
helper_constants::{
|
||||
METADATA, OPTIONAL_EXT_SUFFIX, PLUS_SIGN, POSSIBLE_CHARS_AFTER_EXT_LABEL,
|
||||
POSSIBLE_SEPARATORS_BETWEEN_NUMBER_AND_EXT_LABEL, RFC3966_EXTN_PREFIX, RFC3966_PREFIX,
|
||||
},
|
||||
};
|
||||
|
||||
/// Loads metadata from helper constants METADATA array
|
||||
pub(super) fn load_compiled_metadata() -> Result<PhoneMetadataCollection, protobuf::Error> {
|
||||
let result = PhoneMetadataCollection::parse_from_bytes(&METADATA)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Returns a pointer to the description inside the metadata of the appropriate
|
||||
/// type.
|
||||
pub(super) fn get_number_desc_by_type(
|
||||
metadata: &PhoneMetadata,
|
||||
phone_number_type: PhoneNumberType,
|
||||
) -> &PhoneNumberDesc {
|
||||
match phone_number_type {
|
||||
PhoneNumberType::PremiumRate => &metadata.premium_rate,
|
||||
PhoneNumberType::TollFree => &metadata.toll_free,
|
||||
PhoneNumberType::Mobile => &metadata.mobile,
|
||||
PhoneNumberType::FixedLine | PhoneNumberType::FixedLineOrMobile => &metadata.fixed_line,
|
||||
PhoneNumberType::SharedCost => &metadata.shared_cost,
|
||||
PhoneNumberType::VoIP => &metadata.voip,
|
||||
PhoneNumberType::PersonalNumber => &metadata.personal_number,
|
||||
PhoneNumberType::Pager => &metadata.pager,
|
||||
PhoneNumberType::UAN => &metadata.uan,
|
||||
PhoneNumberType::VoiceMail => &metadata.voicemail,
|
||||
// Instead of the default case, we only match `Unknown`
|
||||
PhoneNumberType::Unknown => &metadata.general_desc,
|
||||
}
|
||||
}
|
||||
|
||||
/// A helper function that is used by Format and FormatByPattern.
|
||||
pub(super) fn prefix_number_with_country_calling_code(
|
||||
country_calling_code: i32,
|
||||
number_format: PhoneNumberFormat,
|
||||
formatted_number: &mut String,
|
||||
) {
|
||||
if let PhoneNumberFormat::National = number_format {
|
||||
return;
|
||||
}
|
||||
let mut buf = itoa::Buffer::new();
|
||||
let country_calling_code_str = buf.format(country_calling_code);
|
||||
|
||||
// we anyway allocate a new string in concatenation, so we'l do it once
|
||||
// with capacity of resulting string
|
||||
match number_format {
|
||||
PhoneNumberFormat::E164 => {
|
||||
let new_str =
|
||||
fast_cat::concat_str!(PLUS_SIGN, country_calling_code_str, &formatted_number);
|
||||
*formatted_number = new_str;
|
||||
}
|
||||
PhoneNumberFormat::International => {
|
||||
let new_str =
|
||||
fast_cat::concat_str!(PLUS_SIGN, country_calling_code_str, " ", &formatted_number);
|
||||
|
||||
*formatted_number = new_str;
|
||||
}
|
||||
PhoneNumberFormat::RFC3966 => {
|
||||
let new_str = fast_cat::concat_str!(
|
||||
RFC3966_PREFIX,
|
||||
PLUS_SIGN,
|
||||
country_calling_code_str,
|
||||
"-",
|
||||
&formatted_number
|
||||
);
|
||||
|
||||
*formatted_number = new_str;
|
||||
}
|
||||
// here code is already returned
|
||||
PhoneNumberFormat::National => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true when one national number is the suffix of the other or both are
|
||||
// the same.
|
||||
pub(super) fn is_national_number_suffix_of_the_other(
|
||||
first_number: &PhoneNumber,
|
||||
second_number: &PhoneNumber,
|
||||
) -> bool {
|
||||
let mut buf = itoa::Buffer::new();
|
||||
let first_number_national_number = buf.format(first_number.national_number());
|
||||
let mut buf = itoa::Buffer::new();
|
||||
let second_number_national_number = buf.format(second_number.national_number());
|
||||
// Note that HasSuffixString returns true if the numbers are equal.
|
||||
return first_number_national_number.ends_with(second_number_national_number)
|
||||
|| second_number_national_number.ends_with(first_number_national_number);
|
||||
}
|
||||
|
||||
/// Helper method for constructing regular expressions for parsing. Creates an
|
||||
/// expression that captures up to max_length digits.
|
||||
pub(super) fn extn_digits(max_length: u32) -> String {
|
||||
let mut buf = itoa::Buffer::new();
|
||||
let max_length_str = buf.format(max_length);
|
||||
const HELPER_STR_LEN: usize = 2 + 4 + 2;
|
||||
|
||||
let mut expr = String::with_capacity(
|
||||
HELPER_STR_LEN + super::helper_constants::DIGITS.len() + max_length_str.len(),
|
||||
);
|
||||
|
||||
expr.push_str("([");
|
||||
// Fully qualify DIGITS const as its common name
|
||||
expr.push_str(super::helper_constants::DIGITS);
|
||||
expr.push_str("]{1,");
|
||||
expr.push_str(max_length_str);
|
||||
expr.push_str("})");
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
// Helper initialiser method to create the regular-expression pattern to match
|
||||
// extensions. Note that:
|
||||
// - There are currently six capturing groups for the extension itself. If this
|
||||
// number is changed, MaybeStripExtension needs to be updated.
|
||||
// - The only capturing groups should be around the digits that you want to
|
||||
// capture as part of the extension, or else parsing will fail!
|
||||
pub(super) fn create_extn_pattern(for_parsing: bool) -> String {
|
||||
// We cap the maximum length of an extension based on the ambiguity of the
|
||||
// way the extension is prefixed. As per ITU, the officially allowed
|
||||
// length for extensions is actually 40, but we don't support this since we
|
||||
// haven't seen real examples and this introduces many false interpretations
|
||||
// as the extension labels are not standardized.
|
||||
let ext_limit_after_explicit_label = 20;
|
||||
let ext_limit_after_likely_label = 15;
|
||||
let ext_limit_after_ambiguous_char = 9;
|
||||
let ext_limit_when_not_sure = 6;
|
||||
|
||||
// Canonical-equivalence doesn't seem to be an option with RE2, so we allow
|
||||
// two options for representing any non-ASCII character like ó - the character
|
||||
// itself, and one in the unicode decomposed form with the combining acute
|
||||
// accent.
|
||||
|
||||
// Here the extension is called out in a more explicit way, i.e mentioning it
|
||||
// obvious patterns like "ext.".
|
||||
let explicit_ext_labels = "(?:e?xt(?:ensi(?:o\u{0301}?|\u{00F3}))?n?|(?:\u{FF45})?\u{FF58}\u{FF54}(?:\u{FF4E})?|\u{0434}\u{043E}\u{0431}|anexo)";
|
||||
// One-character symbols that can be used to indicate an extension, and less
|
||||
// commonly used or more ambiguous extension labels.
|
||||
let ambiguous_ext_labels = "(?:[x\u{FF58}#\u{FF03}~\u{FF5E}]|int|\u{FF49}\u{FF4E}\u{FF54})";
|
||||
// When extension is not separated clearly.
|
||||
let ambiguous_separator = "[- ]+";
|
||||
|
||||
let rfc_extn = fast_cat::concat_str!(
|
||||
RFC3966_EXTN_PREFIX,
|
||||
&extn_digits(ext_limit_after_explicit_label)
|
||||
);
|
||||
let explicit_extn = fast_cat::concat_str!(
|
||||
POSSIBLE_SEPARATORS_BETWEEN_NUMBER_AND_EXT_LABEL,
|
||||
explicit_ext_labels,
|
||||
POSSIBLE_CHARS_AFTER_EXT_LABEL,
|
||||
&extn_digits(ext_limit_after_explicit_label),
|
||||
OPTIONAL_EXT_SUFFIX
|
||||
);
|
||||
let ambiguous_extn = fast_cat::concat_str!(
|
||||
POSSIBLE_SEPARATORS_BETWEEN_NUMBER_AND_EXT_LABEL,
|
||||
ambiguous_ext_labels,
|
||||
POSSIBLE_CHARS_AFTER_EXT_LABEL,
|
||||
&extn_digits(ext_limit_after_ambiguous_char),
|
||||
OPTIONAL_EXT_SUFFIX
|
||||
);
|
||||
|
||||
let american_style_extn_with_suffix = fast_cat::concat_str!(
|
||||
ambiguous_separator,
|
||||
&extn_digits(ext_limit_when_not_sure),
|
||||
"#"
|
||||
);
|
||||
|
||||
// The first regular expression covers RFC 3966 format, where the extension is
|
||||
// added using ";ext=". The second more generic where extension is mentioned
|
||||
// with explicit labels like "ext:". In both the above cases we allow more
|
||||
// numbers in extension than any other extension labels. The third one
|
||||
// captures when single character extension labels or less commonly used
|
||||
// labels are present. In such cases we capture fewer extension digits in
|
||||
// order to reduce the chance of falsely interpreting two numbers beside each
|
||||
// other as a number + extension. The fourth one covers the special case of
|
||||
// American numbers where the extension is written with a hash at the end,
|
||||
// such as "- 503#".
|
||||
let extension_pattern = fast_cat::concat_str!(
|
||||
&rfc_extn,
|
||||
"|",
|
||||
&explicit_extn,
|
||||
"|",
|
||||
&ambiguous_extn,
|
||||
"|",
|
||||
&american_style_extn_with_suffix
|
||||
);
|
||||
// Additional pattern that is supported when parsing extensions, not when
|
||||
// matching.
|
||||
if for_parsing {
|
||||
// ",," is commonly used for auto dialling the extension when connected.
|
||||
// Semi-colon works in Iphone and also in Android to pop up a button with
|
||||
// the extension number following.
|
||||
let auto_dialling_and_ext_labels_found = "(?:,{2}|;)";
|
||||
// This is same as kPossibleSeparatorsBetweenNumberAndExtLabel, but not
|
||||
// matching comma as extension label may have it.
|
||||
let possible_separators_number_ext_label_no_comma = "[ \u{00A0}\t]*";
|
||||
|
||||
let auto_dialling_extn = fast_cat::concat_str!(
|
||||
possible_separators_number_ext_label_no_comma,
|
||||
auto_dialling_and_ext_labels_found,
|
||||
POSSIBLE_CHARS_AFTER_EXT_LABEL,
|
||||
&extn_digits(ext_limit_after_likely_label),
|
||||
OPTIONAL_EXT_SUFFIX
|
||||
);
|
||||
let only_commas_extn = fast_cat::concat_str!(
|
||||
possible_separators_number_ext_label_no_comma,
|
||||
"(?:,)+",
|
||||
POSSIBLE_CHARS_AFTER_EXT_LABEL,
|
||||
&extn_digits(ext_limit_after_ambiguous_char),
|
||||
OPTIONAL_EXT_SUFFIX
|
||||
);
|
||||
// Here the first pattern is exclusive for extension autodialling formats
|
||||
// which are used when dialling and in this case we accept longer
|
||||
// extensions. However, the second pattern is more liberal on number of
|
||||
// commas that acts as extension labels, so we have strict cap on number of
|
||||
// digits in such extensions.
|
||||
return fast_cat::concat_str!(
|
||||
&extension_pattern,
|
||||
"|",
|
||||
&auto_dialling_extn,
|
||||
"|",
|
||||
&only_commas_extn
|
||||
);
|
||||
}
|
||||
return extension_pattern;
|
||||
}
|
||||
|
||||
/// Normalizes a string of characters representing a phone number by replacing
|
||||
/// all characters found in the accompanying map with the values therein, and
|
||||
/// stripping all other characters if remove_non_matches is true.
|
||||
///
|
||||
/// Parameters:
|
||||
/// * `number` - a pointer to a string of characters representing a phone number to
|
||||
/// be normalized.
|
||||
/// * `normalization_replacements` - a mapping of characters to what they should be
|
||||
/// replaced by in the normalized version of the phone number
|
||||
/// * `remove_non_matches` - indicates whether characters that are not able to be
|
||||
/// replaced should be stripped from the number. If this is false, they will be
|
||||
/// left unchanged in the number.
|
||||
pub(super) fn normalize_helper(
|
||||
normalization_replacements: &HashMap<char, char>,
|
||||
remove_non_matches: bool,
|
||||
phone_number: &mut String,
|
||||
) {
|
||||
let mut normalized_number = String::with_capacity(phone_number.len());
|
||||
// Skip UTF checking because strings in rust are valid UTF-8 already
|
||||
for phone_char in phone_number.chars() {
|
||||
if let Some(replacement) = normalization_replacements.get(&phone_char) {
|
||||
normalized_number.push(*replacement);
|
||||
} else if !remove_non_matches {
|
||||
normalized_number.push(phone_char);
|
||||
}
|
||||
// If neither of the above are true, we remove this character.
|
||||
}
|
||||
|
||||
*phone_number = normalized_number;
|
||||
}
|
||||
|
||||
/// Returns `true` if there is any possible number data set for a particular
|
||||
/// PhoneNumberDesc.
|
||||
pub(super) fn desc_has_possible_number_data(desc: &PhoneNumberDesc) -> bool {
|
||||
// If this is empty, it means numbers of this type inherit from the "general
|
||||
// desc" -> the value "-1" means that no numbers exist for this type.
|
||||
return desc.possible_length.len() != 1
|
||||
|| desc
|
||||
.possible_length
|
||||
.get(0)
|
||||
.and_then(|l| Some(*l != -1))
|
||||
.unwrap_or(false);
|
||||
}
|
||||
|
||||
/// Note: `DescHasData` must account for any of MetadataFilter's
|
||||
/// excludableChildFields potentially being absent from the metadata. It must
|
||||
/// check them all. For any changes in `DescHasData`, ensure that all the
|
||||
/// excludableChildFields are still being checked.
|
||||
///
|
||||
/// If your change is safe simply
|
||||
/// mention why during a review without needing to change MetadataFilter.
|
||||
///
|
||||
/// Returns `true` if there is any data set for a particular PhoneNumberDesc.
|
||||
pub(super) fn desc_has_data(desc: &PhoneNumberDesc) -> bool {
|
||||
// Checking most properties since we don't know what's present, since a custom
|
||||
// build may have stripped just one of them (e.g. USE_METADATA_LITE strips
|
||||
// exampleNumber). We don't bother checking the PossibleLengthsLocalOnly,
|
||||
// since if this is the only thing that's present we don't really support the
|
||||
// type at all: no type-specific methods will work with only this data.
|
||||
return desc.has_example_number()
|
||||
|| desc_has_possible_number_data(desc)
|
||||
|| desc.has_national_number_pattern();
|
||||
}
|
||||
|
||||
/// Returns the types we have metadata for based on the PhoneMetadata object
|
||||
/// passed in.
|
||||
pub(super) fn get_supported_types_for_metadata(
|
||||
metadata: &PhoneMetadata,
|
||||
types: &mut HashSet<PhoneNumberType>,
|
||||
) {
|
||||
PhoneNumberType::iter()
|
||||
// Never return FIXED_LINE_OR_MOBILE (it is a convenience type, and
|
||||
// represents that a particular number type can't be
|
||||
// determined) or UNKNOWN (the non-type).
|
||||
.filter(|number_type| {
|
||||
!matches!(
|
||||
number_type,
|
||||
PhoneNumberType::FixedLineOrMobile | PhoneNumberType::Unknown
|
||||
)
|
||||
})
|
||||
.filter(|number_type| desc_has_data(get_number_desc_by_type(metadata, *number_type)))
|
||||
.for_each(|number_type| {
|
||||
types.insert(number_type);
|
||||
});
|
||||
}
|
||||
|
||||
/// Helper method to check a number against possible lengths for this number
|
||||
/// type, and determine whether it matches, or is too short or too long.
|
||||
pub(super) fn test_number_length(
|
||||
phone_number: &str,
|
||||
phone_metadata: &PhoneMetadata,
|
||||
phone_number_type: PhoneNumberType,
|
||||
) -> Result<ValidNumberLenType, ValidationResultErr> {
|
||||
let desc_for_type = get_number_desc_by_type(phone_metadata, phone_number_type);
|
||||
// There should always be "possibleLengths" set for every element. This is
|
||||
// declared in the XML schema which is verified by
|
||||
// PhoneNumberMetadataSchemaTest. For size efficiency, where a
|
||||
// sub-description (e.g. fixed-line) has the same possibleLengths as the
|
||||
// parent, this is missing, so we fall back to the general desc (where no
|
||||
// numbers of the type exist at all, there is one possible length (-1) which
|
||||
// is guaranteed not to match the length of any real phone number).
|
||||
let mut possible_lengths = if desc_for_type.possible_length.len() == 0 {
|
||||
phone_metadata.general_desc.possible_length.clone()
|
||||
} else {
|
||||
desc_for_type.possible_length.clone()
|
||||
};
|
||||
|
||||
let mut local_lengths = desc_for_type.possible_length_local_only.clone();
|
||||
if phone_number_type == PhoneNumberType::FixedLineOrMobile {
|
||||
let fixed_line_desc = get_number_desc_by_type(phone_metadata, PhoneNumberType::FixedLine);
|
||||
if !desc_has_possible_number_data(fixed_line_desc) {
|
||||
// The rare case has been encountered where no fixedLine data is available
|
||||
// (true for some non-geographical entities), so we just check mobile.
|
||||
return test_number_length(phone_number, phone_metadata, PhoneNumberType::Mobile);
|
||||
} else {
|
||||
let mobile_desc = get_number_desc_by_type(phone_metadata, PhoneNumberType::Mobile);
|
||||
if desc_has_possible_number_data(mobile_desc) {
|
||||
// Merge the mobile data in if there was any. Note that when adding the
|
||||
// possible lengths from mobile, we have to again check they aren't
|
||||
// empty since if they are this indicates they are the same as the
|
||||
// general desc and should be obtained from there.
|
||||
|
||||
// RUST NOTE: since merge adds elements to the end of the list, we can do the same
|
||||
let len_to_append = if mobile_desc.possible_length.len() == 0 {
|
||||
&phone_metadata.general_desc.possible_length
|
||||
} else {
|
||||
&mobile_desc.possible_length
|
||||
};
|
||||
possible_lengths.extend_from_slice(len_to_append);
|
||||
possible_lengths.sort();
|
||||
|
||||
if local_lengths.len() == 0 {
|
||||
local_lengths = mobile_desc.possible_length_local_only.clone();
|
||||
} else {
|
||||
local_lengths.extend_from_slice(&mobile_desc.possible_length_local_only);
|
||||
local_lengths.sort();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the type is not suported at all (indicated by the possible lengths
|
||||
// containing -1 at this point) we return invalid length.
|
||||
if *possible_lengths.first().unwrap_or(&-1) == -1 {
|
||||
return Err(ValidationResultErr::InvalidLength);
|
||||
}
|
||||
|
||||
let actual_length = phone_number.len() as i32;
|
||||
// This is safe because there is never an overlap beween the possible lengths
|
||||
// and the local-only lengths; this is checked at build time.
|
||||
if local_lengths.contains(&actual_length) {
|
||||
return Ok(ValidNumberLenType::IsPossibleLocalOnly);
|
||||
}
|
||||
|
||||
// here we can unwrap safe
|
||||
let minimum_length = possible_lengths[0];
|
||||
|
||||
if minimum_length == actual_length {
|
||||
return Ok(ValidNumberLenType::IsPossible);
|
||||
} else if minimum_length > actual_length {
|
||||
return Err(ValidationResultErr::TooShort);
|
||||
} else if possible_lengths[possible_lengths.len() - 1] < actual_length {
|
||||
return Err(ValidationResultErr::TooLong);
|
||||
}
|
||||
// We skip the first element; we've already checked it.
|
||||
return if possible_lengths[1..].contains(&actual_length) {
|
||||
Ok(ValidNumberLenType::IsPossible)
|
||||
} else {
|
||||
Err(ValidationResultErr::InvalidLength)
|
||||
};
|
||||
}
|
||||
|
||||
/// Helper method to check a number against possible lengths for this region,
|
||||
/// based on the metadata being passed in, and determine whether it matches, or
|
||||
/// is too short or too long.
|
||||
pub(super) fn test_number_length_with_unknown_type(
|
||||
phone_number: &str,
|
||||
phone_metadata: &PhoneMetadata,
|
||||
) -> Result<ValidNumberLenType, ValidationResultErr> {
|
||||
return test_number_length(phone_number, phone_metadata, PhoneNumberType::Unknown);
|
||||
}
|
||||
|
||||
/// Returns a new phone number containing only the fields needed to uniquely
|
||||
/// identify a phone number, rather than any fields that capture the context in
|
||||
/// which the phone number was created.
|
||||
/// These fields correspond to those set in `parse()` rather than
|
||||
/// `parse_and_keep_raw_input()`.
|
||||
pub(crate) fn copy_core_fields_only(from_number: &PhoneNumber, to_number: &mut PhoneNumber) {
|
||||
to_number.set_country_code(from_number.country_code());
|
||||
to_number.set_national_number(from_number.national_number());
|
||||
if let Some(extension) = &from_number.extension {
|
||||
to_number.set_extension(extension.clone());
|
||||
}
|
||||
if from_number.italian_leading_zero() {
|
||||
to_number.set_italian_leading_zero(true);
|
||||
// This field is only relevant if there are leading zeros at all.
|
||||
to_number.set_number_of_leading_zeros(from_number.number_of_leading_zeros());
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines whether the given number is a national number match for the given
|
||||
/// PhoneNumberDesc. Does not check against possible lengths!
|
||||
pub(super) fn is_match(
|
||||
matcher_api: Box<dyn MatcherApi>,
|
||||
number: &str,
|
||||
number_desc: &PhoneNumberDesc,
|
||||
) -> bool {
|
||||
matcher_api.match_national_number(number, number_desc, false)
|
||||
}
|
||||
24
src/phonenumberutil/mod.rs
Normal file
24
src/phonenumberutil/mod.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
mod helper_constants;
|
||||
pub mod helper_functions;
|
||||
mod enums;
|
||||
mod phonenumberutil;
|
||||
mod regex_and_mappings;
|
||||
|
||||
pub use enums::{MatchType, PhoneNumberFormat, PhoneNumberType, ValidationResultErr, ValidNumberLenType};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ErrorType {
|
||||
#[error("No parsing")]
|
||||
NoParsingError,
|
||||
#[error("Invalid country code")]
|
||||
InvalidCountryCodeError, // INVALID_COUNTRY_CODE in the java version.
|
||||
#[error("Not a number")]
|
||||
NotANumber,
|
||||
#[error("Too short after idd")]
|
||||
TooShortAfterIdd,
|
||||
#[error("Too short Nsn")]
|
||||
TooShortNsn,
|
||||
#[error("Too long nsn")]
|
||||
TooLongNsn, // TOO_LONG in the java version.
|
||||
}
|
||||
34
src/phonenumberutil/phonenumberutil.rs
Normal file
34
src/phonenumberutil/phonenumberutil.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::regex_and_mappings::PhoneNumberRegExpsAndMappings;
|
||||
use crate::{interfaces::MatcherApi, proto_gen::phonemetadata::PhoneMetadata};
|
||||
|
||||
use dashmap::{DashMap, DashSet};
|
||||
|
||||
pub struct PhoneNumberUtil {
|
||||
/// An API for validation checking.
|
||||
matcher_api: Box<dyn MatcherApi>,
|
||||
|
||||
/// Helper class holding useful regular expressions and character mappings.
|
||||
reg_exps: Arc<PhoneNumberRegExpsAndMappings>,
|
||||
|
||||
/// A mapping from a country calling code to a RegionCode object which denotes
|
||||
/// the region represented by that country calling code. Note regions under
|
||||
/// NANPA share the country calling code 1 and Russia and Kazakhstan share the
|
||||
/// country calling code 7. Under this map, 1 is mapped to region code "US" and
|
||||
/// 7 is mapped to region code "RU". This is implemented as a sorted vector to
|
||||
/// achieve better performance.
|
||||
country_calling_code_to_region_code_map: Vec<(i32, String)>,
|
||||
|
||||
/// The set of regions that share country calling code 1.
|
||||
nanpa_regions: DashSet<String>,
|
||||
|
||||
/// A mapping from a region code to a PhoneMetadata for that region.
|
||||
region_to_metadata_map: DashMap<String, PhoneMetadata>,
|
||||
|
||||
// A mapping from a country calling code for a non-geographical entity to the
|
||||
// PhoneMetadata for that country calling code. Examples of the country
|
||||
// calling codes include 800 (International Toll Free Service) and 808
|
||||
// (International Shared Cost Service).
|
||||
country_code_to_non_geographical_metadata_map: DashMap<u32, PhoneMetadata>,
|
||||
}
|
||||
3
src/phonenumberutil/regex_and_mappings.rs
Normal file
3
src/phonenumberutil/regex_and_mappings.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub struct PhoneNumberRegExpsAndMappings{
|
||||
|
||||
}
|
||||
1
src/proto_gen/mod.rs
Normal file
1
src/proto_gen/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
include!(concat!(env!("OUT_DIR"), "/proto_gen/mod.rs"));
|
||||
37
src/regexp_cache.rs
Normal file
37
src/regexp_cache.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use dashmap::DashMap;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[error("An error occurred while trying to create regex: {0}")]
|
||||
pub struct ErrorInvalidRegex(#[from] regex::Error);
|
||||
|
||||
pub struct RegexCache {
|
||||
cache: DashMap<String, Arc<regex::Regex>>
|
||||
}
|
||||
|
||||
impl RegexCache {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
cache: DashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
Self {
|
||||
cache: DashMap::with_capacity(capacity),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_regex(&self, pattern: &str) -> Result<Arc<regex::Regex>, ErrorInvalidRegex> {
|
||||
if let Some(regex) = self.cache.get(pattern) {
|
||||
Ok(regex.value().clone())
|
||||
} else {
|
||||
let entry = self.cache.entry(pattern.to_string()).or_try_insert_with(|| {
|
||||
regex::Regex::new(pattern).map(Arc::new)
|
||||
})?;
|
||||
Ok(entry.value().clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user