| Crates.io | phonelib |
| lib.rs | phonelib |
| version | 1.0.3 |
| created_at | 2023-12-09 20:38:40.006455+00 |
| updated_at | 2026-01-22 22:06:07.053538+00 |
| description | A comprehensive library for phone number validation, formatting, parsing, and manipulation |
| homepage | https://github.com/mohamadzoh/phonelib |
| repository | https://github.com/mohamadzoh/phonelib |
| max_upload_size | |
| id | 1063885 |
| size | 195,537 |
Phonelib is a comprehensive Rust library for handling phone numbers. It provides functions for validation, formatting, type detection, text extraction, batch processing, and much more.
PhoneNumber structAdd this to your Cargo.toml:
[dependencies]
phonelib = "1.0.1"
use phonelib::*;
// Basic validation
let is_valid = is_valid_phone_number("+12025550173");
// Format a number
let formatted = format_phone_number(
"12025550173",
PhoneFormat::International
);
// Detect number type
let number_type = detect_phone_number_type("+442079460958");
// Extract phone numbers from text
let text = "Call me at +12025550173 or +442079460958";
let numbers = extract_phone_numbers_from_text(text);
// Compare phone numbers (PhoneNumber struct with Eq trait)
let num1 = PhoneNumber::parse("+12025550173").unwrap();
let num2 = PhoneNumber::parse("12025550173").unwrap();
assert_eq!(num1, num2); // Same number, different formats
pub struct Country {
pub name: &'static str,
pub code: &'static str,
pub phone_lengths: &'static [u8],
pub prefix: u32,
}
pub enum PhoneFormat {
E164, // +1234567890
International, // +1 234 567-890
National, // (234) 567-890
RFC3966, // tel:+1-234-567-890
}
pub enum PhoneNumberType {
Mobile, FixedLine, TollFree, PremiumRate,
SharedCost, Voip, PersonalNumber, Pager,
Uan, Emergency, Voicemail, Unknown,
}
use phonelib::is_valid_phone_number;
let phone_number = "+1234567890";
if is_valid_phone_number(phone_number) {
println!("Valid phone number!");
}
use phonelib::extract_country;
let phone_number = "+1234567890";
match extract_country(phone_number) {
Some(country) => println!("Country: {} ({})", country.name, country.code),
None => println!("Country not found"),
}
use phonelib::{normalize_phone_number, normalize_phone_number_in_place};
// Returns normalized number without modifying input
let normalized = normalize_phone_number("+1 (234) 567-890");
println!("Normalized: {:?}", normalized); // Some("+1234567890")
// Modifies the input string in place
let mut phone = "+1 (234) 567-890".to_string();
normalize_phone_number_in_place(&mut phone);
println!("In-place normalized: {}", phone);
use phonelib::{format_phone_number, PhoneFormat};
let number = "1234567890";
// E.164 format
let e164 = format_phone_number(number, PhoneFormat::E164);
// Result: Some("+1234567890")
// International format
let intl = format_phone_number(number, PhoneFormat::International);
// Result: Some("+1 234 567-890")
// National format
let national = format_phone_number(number, PhoneFormat::National);
// Result: Some("(234) 567-890")
// RFC3966 format
let rfc = format_phone_number(number, PhoneFormat::RFC3966);
// Result: Some("tel:+1-234-567-890")
use phonelib::{
detect_phone_number_type, is_mobile_number,
is_landline_number, is_toll_free_number, PhoneNumberType
};
let mobile = "447123456789";
let landline = "442079460958";
let toll_free = "18001234567";
// Detect specific type
match detect_phone_number_type(mobile) {
Some(PhoneNumberType::Mobile) => println!("It's a mobile number!"),
Some(other_type) => println!("It's a {:?}", other_type),
None => println!("Invalid or unknown type"),
}
// Quick type checks
if is_mobile_number(mobile) {
println!("Mobile number detected");
}
if is_landline_number(landline) {
println!("Landline number detected");
}
if is_toll_free_number(toll_free) {
println!("Toll-free number detected");
}
use phonelib::{generate_random_phone_number, generate_random_phone_numbers};
// Generate a single random number
let random_us = generate_random_phone_number("US");
println!("Random US number: {:?}", random_us);
// Generate multiple random numbers
let random_numbers = generate_random_phone_numbers("GB", 5);
println!("5 random UK numbers: {:?}", random_numbers);
use phonelib::{are_phone_numbers_equal, group_equivalent_phone_numbers};
// Compare two numbers
let num1 = "+1234567890";
let num2 = "(234) 567-890";
if are_phone_numbers_equal(num1, num2) {
println!("Numbers are equivalent!");
}
// Group equivalent numbers
let numbers = [
"+1234567890",
"(234) 567-890",
"+9876543210",
"987-654-3210",
];
let groups = group_equivalent_phone_numbers(&numbers);
for (i, group) in groups.iter().enumerate() {
println!("Group {}: {:?}", i + 1, group);
}
use phonelib::{
validate_phone_numbers_batch,
normalize_phone_numbers_batch,
detect_phone_number_types_batch,
analyze_phone_numbers_batch
};
let numbers = [
"1234567890",
"invalid",
"447123456789",
];
// Batch validation
let valid_results = validate_phone_numbers_batch(&numbers);
println!("Validation results: {:?}", valid_results);
// Batch normalization
let normalized_results = normalize_phone_numbers_batch(&numbers);
println!("Normalized results: {:?}", normalized_results);
// Batch type detection
let type_results = detect_phone_number_types_batch(&numbers);
println!("Type results: {:?}", type_results);
// Comprehensive batch analysis
let analyses = analyze_phone_numbers_batch(&numbers);
for analysis in analyses {
println!("Original: {}", analysis.original);
println!("Valid: {}", analysis.is_valid);
println!("Normalized: {:?}", analysis.normalized);
println!("Country: {:?}", analysis.country.map(|c| c.name));
println!("Type: {:?}", analysis.phone_type);
println!("---");
}
use phonelib::{
suggest_phone_number_corrections,
is_potentially_valid_phone_number,
guess_country_from_number
};
// Get suggestions for invalid numbers
let invalid_number = "123456789";
let suggestions = suggest_phone_number_corrections(invalid_number, Some("US"));
println!("Suggestions: {:?}", suggestions);
// Check if a number might be valid with different formatting
let maybe_valid = "123-456-7890";
if is_potentially_valid_phone_number(maybe_valid) {
println!("This number might be valid with proper formatting");
}
// Guess country from number patterns
let mystery_number = "442079460958";
match guess_country_from_number(mystery_number) {
Some(country) => println!("Likely from: {}", country.name),
None => println!("Cannot determine country"),
}
Extract phone numbers from free-form text:
use phonelib::{
extract_phone_numbers_from_text,
extract_valid_phone_numbers_from_text,
extract_phone_numbers_with_country_hint,
replace_phone_numbers_in_text,
redact_phone_numbers,
count_phone_numbers_in_text,
};
let text = "Contact us at +12025550173 or call our UK office at +442079460958";
// Extract all phone numbers
let numbers = extract_phone_numbers_from_text(text);
for num in &numbers {
println!("Found: {} at position {}-{}", num.raw, num.start, num.end);
println!(" Normalized: {:?}", num.normalized);
println!(" Valid: {}", num.is_valid);
}
// Extract only valid numbers
let valid_numbers = extract_valid_phone_numbers_from_text(text);
// Extract with country hint for national numbers
let us_text = "Call (202) 555-0173 for assistance";
let numbers = extract_phone_numbers_with_country_hint(us_text, "US");
// Count phone numbers
let count = count_phone_numbers_in_text(text);
println!("Found {} phone numbers", count);
// Replace phone numbers
let replaced = replace_phone_numbers_in_text(text, |num| {
format!("[PHONE: {}]", num.normalized.as_deref().unwrap_or(&num.raw))
});
// Redact for privacy (show last 4 digits)
let redacted = redact_phone_numbers(text, 4);
println!("{}", redacted); // "Contact us at ********0173 or..."
The PhoneNumber struct provides type-safe phone number handling with proper equality comparison:
use phonelib::{PhoneNumber, PhoneNumberSet, PhoneFormat};
use std::collections::HashSet;
// Parse phone numbers
let num1 = PhoneNumber::parse("+12025550173").unwrap();
let num2 = PhoneNumber::parse("12025550173").unwrap();
let num3 = PhoneNumber::parse("+442079460958").unwrap();
// Equality comparison (same number, different formats)
assert_eq!(num1, num2);
assert_ne!(num1, num3);
// Use in HashSet for deduplication
let mut set = HashSet::new();
set.insert(num1.clone());
set.insert(num2.clone()); // Won't be added - duplicate
assert_eq!(set.len(), 1);
// PhoneNumber methods
println!("E.164: {}", num1.e164());
println!("National: {}", num1.national_number());
println!("Country code: {:?}", num1.country_code());
println!("Is mobile: {}", num1.is_mobile());
println!("Formatted: {}", num1.format(PhoneFormat::International));
// Parse with country hint for national numbers
let national = PhoneNumber::parse_with_country("2025550173", "US");
// PhoneNumberSet for efficient deduplication
let mut phone_set = PhoneNumberSet::new();
phone_set.add("+12025550173");
phone_set.add("12025550173"); // Duplicate - not added
phone_set.add("+442079460958");
assert_eq!(phone_set.len(), 2);
// Check membership
assert!(phone_set.contains("12025550173"));
// Create from iterator
let numbers = vec!["+12025550173", "12025550173", "+442079460958"];
let set: PhoneNumberSet = numbers.into_iter().collect();
assert_eq!(set.len(), 2);
The library supports 246 countries with accurate:
๐บ๐ธ United States & Canada (NANP)
๐ฌ๐ง United Kingdom
๐ฉ๐ช Germany
๐ซ๐ท France
๐ฎ๐ณ India
๐ฆ๐บ Australia
And 240+ more countries worldwide
use phonelib::*;
fn main() {
let numbers = [
"1234567890",
"+44 7123 456789",
"(555) 123-4567",
"invalid-number",
];
for number in numbers {
println!("\n--- Analyzing: {} ---", number);
// Basic validation
let is_valid = is_valid_phone_number(number);
println!("Valid: {}", is_valid);
if is_valid {
// Format in different styles
if let Some(e164) = format_phone_number(number, PhoneFormat::E164) {
println!("E.164: {}", e164);
}
if let Some(intl) = format_phone_number(number, PhoneFormat::International) {
println!("International: {}", intl);
}
// Detect country
if let Some(country) = extract_country(number) {
println!("Country: {} ({})", country.name, country.code);
}
// Detect type
if let Some(phone_type) = detect_phone_number_type(number) {
println!("Type: {:?}", phone_type);
}
} else {
// Suggest corrections
let suggestions = suggest_phone_number_corrections(number, Some("US"));
if !suggestions.is_empty() {
println!("Suggestions: {:?}", suggestions);
}
}
}
}
Contributions to the Phonelib library are welcome! Here's how you can help:
# Clone the repository
git clone https://github.com/mohamadzoh/phonelib.git
cd phonelib
# Run tests
cargo test
# Run benchmarks
cargo bench
# Check code formatting
cargo fmt
# Run clippy for linting
cargo clippy
Minor Bug Fixes
First Stable Release
This release marks the first stable version of phonelib, with a production-ready API.
&str instead of String for better ergonomics
.to_string() calls needed!is_valid_phone_number("+12025550173") just works&[T] where T: AsRef<str> instead of Vec<String>
&str or Stringvalidate_phone_numbers_batch(&["123", "456"]) just worksGB-CYM๐ด๓ ง๓ ข๓ ท๓ ฌ๓ ณ๓ ฟ Cymru Support
GB-CYMText Extraction & Equality Release
extract_phone_numbers_from_text - Find all phone numbers in textextract_valid_phone_numbers_from_text - Find only valid numbersextract_phone_numbers_with_country_hint - Parse with default countryreplace_phone_numbers_in_text - Custom replacement functionredact_phone_numbers - Privacy-focused maskingcount_phone_numbers_in_text - Quick countEq, PartialEq, Hash for use in collectionsDisplay, FromStr for easy conversione164(), national_number(), format(), is_mobile(), etc.Major Feature Release
This project is licensed under the MIT License - see the LICENSE file for details.
Phonelib is part of the larger Rusty Rails project, which aims to bridge the gap between Rust and Ruby/Ruby on Rails ecosystems. We're actively working on recreating Ruby libraries in Rust to make working with Rust more easy and fun for new developers.
Made with โค๏ธ by the Rusty Rails team