Add some phonenumberutil functions

This commit is contained in:
Vlasislav Kashin
2025-07-09 13:22:32 +03:00
parent 29f5e5664c
commit 7692433296
11 changed files with 1409 additions and 91 deletions

314
Cargo.lock generated
View File

@@ -41,6 +41,27 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "csv"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf"
dependencies = [
"csv-core",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "csv-core"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "dashmap" name = "dashmap"
version = "6.1.0" version = "6.1.0"
@@ -55,6 +76,37 @@ dependencies = [
"parking_lot_core", "parking_lot_core",
] ]
[[package]]
name = "dec_from_char"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5467a30cfe9ac2281f28ab2fc7e82b3036544bcb340f648425769fe9eb737708"
dependencies = [
"dec_from_char_gen",
]
[[package]]
name = "dec_from_char_gen"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec31e992a6f011b864a5eda50b01cf01fa8d9d53241f4902f93d0f8024faa6"
dependencies = [
"csv",
"quote",
"serde",
]
[[package]]
name = "displaydoc"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "either" name = "either"
version = "1.15.0" version = "1.15.0"
@@ -128,6 +180,95 @@ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
[[package]]
name = "icu_collections"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
dependencies = [
"displaydoc",
"potential_utf",
"yoke",
"zerofrom",
"zerovec",
]
[[package]]
name = "icu_locale_core"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
dependencies = [
"displaydoc",
"litemap",
"tinystr",
"writeable",
"zerovec",
]
[[package]]
name = "icu_normalizer"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
dependencies = [
"displaydoc",
"icu_collections",
"icu_normalizer_data",
"icu_properties",
"icu_provider",
"smallvec",
"utf16_iter",
"utf8_iter",
"write16",
"zerovec",
]
[[package]]
name = "icu_normalizer_data"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
[[package]]
name = "icu_properties"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
dependencies = [
"displaydoc",
"icu_collections",
"icu_locale_core",
"icu_properties_data",
"icu_provider",
"potential_utf",
"zerotrie",
"zerovec",
]
[[package]]
name = "icu_properties_data"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
[[package]]
name = "icu_provider"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
dependencies = [
"displaydoc",
"icu_locale_core",
"stable_deref_trait",
"tinystr",
"writeable",
"yoke",
"zerofrom",
"zerotrie",
"zerovec",
]
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.10.0" version = "2.10.0"
@@ -162,6 +303,12 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
[[package]]
name = "litemap"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.13" version = "0.4.13"
@@ -203,6 +350,15 @@ dependencies = [
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
[[package]]
name = "potential_utf"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585"
dependencies = [
"zerovec",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.95" version = "1.0.95"
@@ -321,7 +477,9 @@ name = "rlibphonenumbers"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"dashmap", "dashmap",
"dec_from_char",
"fast-cat", "fast-cat",
"icu_normalizer",
"itoa", "itoa",
"log", "log",
"protobuf", "protobuf",
@@ -329,6 +487,7 @@ dependencies = [
"regex", "regex",
"strum", "strum",
"thiserror 2.0.12", "thiserror 2.0.12",
"tinystr",
] ]
[[package]] [[package]]
@@ -363,18 +522,50 @@ version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.2.0" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.15.1" version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]] [[package]]
name = "strum" name = "strum"
version = "0.27.1" version = "0.27.1"
@@ -408,6 +599,17 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "synstructure"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.20.0" version = "3.20.0"
@@ -461,12 +663,34 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "tinystr"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
dependencies = [
"displaydoc",
"zerovec",
]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.18" version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "utf16_iter"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
[[package]]
name = "utf8_iter"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.14.2+wasi-0.2.4" version = "0.14.2+wasi-0.2.4"
@@ -642,3 +866,93 @@ checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [ dependencies = [
"bitflags", "bitflags",
] ]
[[package]]
name = "write16"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
[[package]]
name = "writeable"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
[[package]]
name = "yoke"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
dependencies = [
"serde",
"stable_deref_trait",
"yoke-derive",
"zerofrom",
]
[[package]]
name = "yoke-derive"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "zerofrom"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
dependencies = [
"zerofrom-derive",
]
[[package]]
name = "zerofrom-derive"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "zerotrie"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
dependencies = [
"displaydoc",
"yoke",
"zerofrom",
]
[[package]]
name = "zerovec"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
dependencies = [
"yoke",
"zerofrom",
"zerovec-derive",
]
[[package]]
name = "zerovec-derive"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View File

@@ -22,6 +22,9 @@ itoa = "1.0.15"
# concatenation of strings # concatenation of strings
fast-cat = "0.1.1" fast-cat = "0.1.1"
strum = { version = "0.27.1", features = ["derive"] } strum = { version = "0.27.1", features = ["derive"] }
icu_normalizer = "2.0.0"
tinystr = "0.8.1"
dec_from_char = "0.1.1"
[build-dependencies] [build-dependencies]
thiserror = "2.0.12" thiserror = "2.0.12"

View File

@@ -72,6 +72,7 @@ fn parse_prefixes(path: &str, prefixes: &mut BTreeMap<i32, String>) -> Result<()
fn main() -> Result<(), BuildError> { fn main() -> Result<(), BuildError> {
protobuf_codegen::Codegen::new() protobuf_codegen::Codegen::new()
.pure()
.includes(["resources"]) .includes(["resources"])
.input("resources/phonemetadata.proto") .input("resources/phonemetadata.proto")
.input("resources/phonenumber.proto") .input("resources/phonenumber.proto")

View File

@@ -6,7 +6,8 @@ mod phonenumberutil;
mod regexp_cache; mod regexp_cache;
mod regex_based_matcher; mod regex_based_matcher;
pub mod i18n; pub mod i18n;
pub mod regex_util; pub(crate) mod regex_util;
pub(crate) mod string_util;
/// I decided to create this module because there are many /// I decided to create this module because there are many
/// boilerplate places in the code that can be replaced with macros, /// boilerplate places in the code that can be replaced with macros,

View File

@@ -1,3 +1,6 @@
use core::error;
use std::num::ParseIntError;
use thiserror::Error; use thiserror::Error;
use crate::regexp_cache::ErrorInvalidRegex; use crate::regexp_cache::ErrorInvalidRegex;
@@ -5,5 +8,38 @@ use crate::regexp_cache::ErrorInvalidRegex;
#[derive(Debug, PartialEq, Error)] #[derive(Debug, PartialEq, Error)]
pub enum PhoneNumberUtilError { pub enum PhoneNumberUtilError {
#[error("{0}")] #[error("{0}")]
InvalidRegexError(#[from] ErrorInvalidRegex) InvalidRegexError(#[from] ErrorInvalidRegex),
} #[error("Parse error: {0}")]
ParseError(#[from] ParseError),
#[error("Extract number error: {0}")]
ExtractNumberError(#[from] ExtractNumberError)
}
#[derive(Debug, PartialEq, Error)]
pub enum ParseError {
// Removed as OK variant
// 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.
#[error("{0}")]
InvalidRegexError(#[from] ErrorInvalidRegex),
#[error("{0}")]
ParseNumberAsIntError(#[from] ParseIntError)
}
#[derive(Debug, PartialEq, Error)]
pub enum ExtractNumberError {
#[error("No valid start character found")]
NoValidStartCharacter,
#[error("Invalid number")]
NotANumber,
}

View File

@@ -0,0 +1,31 @@
use std::borrow::Cow;
use crate::proto_gen::phonenumber::phone_number::CountryCodeSource;
#[derive(Debug)]
pub struct PhoneNumberWithCountryCodeSource<'a> {
pub phone_number: Cow<'a, str>,
pub country_code_source: CountryCodeSource
}
impl<'a> PhoneNumberWithCountryCodeSource<'a> {
pub fn new(phone_number: Cow<'a, str>, country_code_source: CountryCodeSource) -> Self {
Self { phone_number, country_code_source }
}
}
#[derive(Debug)]
pub struct PhoneNumberAndCarrierCode<'a> {
pub carrier_code: Option<&'a str>,
pub phone_number: Cow<'a, str>
}
impl<'a> PhoneNumberAndCarrierCode<'a> {
pub fn new<B: Into<Cow<'a, str>>>(carrier_code: Option<&'a str>, phone_number: B) -> Self {
Self { carrier_code, phone_number: phone_number.into() }
}
pub fn new_phone<B: Into<Cow<'a, str>>>(phone_number: B) -> Self {
Self { carrier_code: None, phone_number: phone_number.into() }
}
}

View File

@@ -4,30 +4,15 @@ mod errors;
mod enums; mod enums;
mod phonenumberutil; mod phonenumberutil;
mod phone_number_regexps_and_mappings; mod phone_number_regexps_and_mappings;
pub(self) mod helper_types;
use std::sync::LazyLock; use std::sync::LazyLock;
pub use enums::{MatchType, PhoneNumberFormat, PhoneNumberType, ValidationResultErr, ValidNumberLenType}; pub use enums::{MatchType, PhoneNumberFormat, PhoneNumberType, ValidationResultErr, ValidNumberLenType};
use thiserror::Error; use thiserror::Error;
use crate::phonenumberutil::phonenumberutil::PhoneNumberUtil; // use crate::phonenumberutil::phonenumberutil::PhoneNumberUtil;
#[derive(Debug, Error)] // static PHONE_NUMBER_UTIL: LazyLock<PhoneNumberUtil> = LazyLock::new(|| {
pub enum ErrorType { // PhoneNumberUtil::new()
#[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.
}
static PHONE_NUMBER_UTIL: LazyLock<PhoneNumberUtil> = LazyLock::new(|| {
PhoneNumberUtil::new()
});

File diff suppressed because it is too large Load Diff

View File

@@ -21,7 +21,7 @@ impl RegexBasedMatcher {
// find first occurrence // find first occurrence
if allow_prefix_match { if allow_prefix_match {
Ok(regexp.consume_start(phone_number).is_some()) Ok(regexp.matches_start(phone_number))
} else { } else {
Ok(regexp.full_match(phone_number)) Ok(regexp.full_match(phone_number))
} }

View File

@@ -1,6 +1,4 @@
use std::borrow::Cow; use regex::{Captures, Match, Regex};
use regex::{Captures, Regex};
pub trait RegexFullMatch { pub trait RegexFullMatch {
/// Eq of C fullMatch /// Eq of C fullMatch
@@ -8,18 +6,12 @@ pub trait RegexFullMatch {
} }
pub trait RegexConsume { pub trait RegexConsume {
/// Eq of C Consume fn matches_start<'a>(&self, s: &'a str) -> bool {
fn consume_start<'a>(&self, s: &'a str) -> Option<Cow<'a, str>> { self.find_start(s).is_some()
self.consume_start_capturing(s).map(| res| res.0)
} }
fn consume_start_capturing<'a>(&self, s: &'a str) -> Option<(Cow<'a, str>, Captures<'a>)>; fn captures_start<'a>(&self, s: &'a str) -> Option<Captures<'a>>;
fn find_start<'a>(&self, s: &'a str) -> Option<Match<'a>>;
fn find_and_consume<'a>(&self, s: &'a str) -> Option<Cow<'a, str>> {
self.find_and_consume_capturing(s).map(| res| res.0)
}
fn find_and_consume_capturing<'a>(&self, s: &'a str) -> Option<(Cow<'a, str>, Captures<'a>)>;
} }
trait RegexMatchStart { trait RegexMatchStart {
@@ -48,24 +40,21 @@ impl RegexMatchStart for Regex {
} }
impl RegexConsume for Regex { impl RegexConsume for Regex {
fn consume_start_capturing<'a>(&self, s: &'a str) -> Option<(Cow<'a, str>, Captures<'a>)> { fn captures_start<'a>(&self, s: &'a str) -> Option<Captures<'a>> {
_consume(self, s, true) let captures = self.captures(s)?;
let full_capture = captures.get(0)?;
if full_capture.start() != 0 {
return None
}
Some(captures)
} }
fn find_and_consume_capturing<'a>(&self, s: &'a str) -> Option<(Cow<'a, str>, Captures<'a>)> { fn find_start<'a>(&self, s: &'a str) -> Option<Match<'a>> {
_consume(self, s, false) let found = self.find(s)?;
if found.start() != 0 {
return None
}
Some(found)
} }
} }
fn _consume<'a>(
r: &Regex, input: &'a str,
anchor_at_start: bool
) -> Option<(Cow<'a, str>, Captures<'a>)> {
let captures = r.captures(input)?;
let full_capture = captures.get(0)?;
if anchor_at_start && full_capture.start() != 0 {
return None
}
Some((Cow::Borrowed(&input[full_capture.end()..]), captures))
}

36
src/string_util.rs Normal file
View File

@@ -0,0 +1,36 @@
use std::borrow::Cow;
/// Strips prefix of given string Cow. Returns option with `Some` if
/// prefix found and stripped.
///
/// Calls `drain` if string is owned and returns slice if string is borrowed
pub fn strip_cow_prefix<'a>(cow: Cow<'a, str>, prefix: &str) -> Option<Cow<'a, str>> {
match cow {
Cow::Borrowed(s) => s.strip_prefix(prefix).map(| s | Cow::Borrowed(s)),
Cow::Owned(mut s) => {
if s.starts_with(prefix) {
s.drain(0..prefix.len());
return Some(Cow::Owned(s));
}
None
}
}
}
#[cfg(test)]
mod tests {
use std::borrow::Cow;
use crate::string_util::strip_cow_prefix;
#[test]
fn test_usage() {
let str_to_strip = Cow::Owned("test0:test".to_owned());
let stripped = strip_cow_prefix(str_to_strip, "test0");
assert_eq!(stripped, Some(Cow::Owned(":test".to_owned())));
let str_to_strip = Cow::Owned("test:test0".to_owned());
let stripped = strip_cow_prefix(str_to_strip, "test0");
assert_eq!(stripped, None)
}
}