#phone-number #validation #international #e164 #phone

phonelib

A comprehensive library for phone number validation, formatting, parsing, and manipulation

11 releases (1 stable)

new 1.0.0 Jan 10, 2026
0.3.0 Jan 10, 2026
0.2.1 Jan 3, 2026
0.2.0 Sep 14, 2025
0.1.1 Dec 9, 2023

#29 in Internationalization (i18n)

Download history 378/week @ 2025-09-26 307/week @ 2025-10-03 312/week @ 2025-10-10 664/week @ 2025-10-17 431/week @ 2025-10-24 222/week @ 2025-10-31 284/week @ 2025-11-07 625/week @ 2025-11-14 737/week @ 2025-11-21 817/week @ 2025-11-28 677/week @ 2025-12-05 556/week @ 2025-12-12 247/week @ 2025-12-19 188/week @ 2025-12-26 1044/week @ 2026-01-02 958/week @ 2026-01-09

2,615 downloads per month

MIT license

160KB
4K SLoC

Phonelib

Crates.io Documentation License: MIT Rust

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.

Features

  • โœ… Phone Number Validation - Check if phone numbers are valid
  • ๐ŸŒ Country Detection - Extract country information from phone numbers
  • ๐Ÿ”ง Normalization - Clean and standardize phone number formats
  • ๐ŸŽจ Multiple Format Support - E.164, International, National, RFC3966
  • ๐Ÿ“ฑ Type Detection - Identify mobile, landline, toll-free, premium numbers
  • ๐Ÿ“ Text Extraction - Parse phone numbers from free-form text
  • โš–๏ธ Comparison & Equality - Compare numbers regardless of format with PhoneNumber struct
  • ๐ŸŽฒ Random Number Generation - Generate valid random phone numbers
  • ๐Ÿš€ Batch Processing - Process multiple numbers efficiently
  • ๐Ÿ” Smart Suggestions - Get correction suggestions for invalid numbers
  • ๐Ÿ”’ Privacy Tools - Redact phone numbers in text

Installation

Add this to your Cargo.toml:

[dependencies]
phonelib = "1.0.0"

Quick Start

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

API Reference

Core Types

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,
}

Basic Functions

Phone Number Validation

use phonelib::is_valid_phone_number;

let phone_number = "+1234567890";
if is_valid_phone_number(phone_number) {
    println!("Valid phone number!");
}

Country Extraction

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"),
}

Phone Number Normalization

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);

Phone Number Formatting

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")

Phone Number Type Detection

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");
}

Random Phone Number Generation

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);

Phone Number Comparison

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);
}

Batch Processing

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!("---");
}

Smart Suggestions & Intelligence

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"),
}

Text Extraction

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..."

PhoneNumber Struct with Equality

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);

Country Support

The library supports 246 countries with accurate:

  • Country codes and prefixes
  • Valid phone number lengths
  • Mobile vs. landline detection patterns
  • Toll-free and premium number identification

Supported Countries Include:

  • ๐Ÿ‡บ๐Ÿ‡ธ United States & Canada (NANP)
  • ๐Ÿ‡ฌ๐Ÿ‡ง United Kingdom
  • ๐Ÿ‡ฉ๐Ÿ‡ช Germany
  • ๐Ÿ‡ซ๐Ÿ‡ท France
  • ๐Ÿ‡ฎ๐Ÿ‡ณ India
  • ๐Ÿ‡ฆ๐Ÿ‡บ Australia
  • And 240+ more countries worldwide

Examples

Complete Example

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);
            }
        }
    }
}

Contributing

Contributions to the Phonelib library are welcome! Here's how you can help:

  • ๐Ÿ› Report bugs - Open an issue if you find any problems
  • ๐Ÿ’ก Suggest features - Share ideas for new functionality
  • ๐Ÿ”ง Submit pull requests - Help improve the code
  • ๐Ÿ“– Improve documentation - Help make the docs better
  • ๐Ÿงช Add tests - Increase test coverage

Development Setup

# 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

Changelog

v1.0.0 (Latest)

First Stable Release

This release marks the first stable version of phonelib, with a production-ready API.

  • Breaking Change: All functions now accept &str instead of String for better ergonomics
    • No more .to_string() calls needed!
    • is_valid_phone_number("+12025550173") just works
  • Breaking Change: Batch functions now accept &[T] where T: AsRef<str> instead of Vec<String>
    • Works with arrays, slices, or Vecs of &str or String
    • validate_phone_numbers_batch(&["123", "456"]) just works
  • Zero allocations for read-only operations
  • Added support for Cymru (Wales) with country code GB-CYM

v0.3.0

๐Ÿด๓ ง๓ ข๓ ท๓ ฌ๓ ณ๓ ฟ Cymru Support

  • Added support for Cymru (Wales) with country code GB-CYM

v0.2.1

Text Extraction & Equality Release

  • ๐Ÿ“ Text Extraction - Extract phone numbers from free-form text
    • extract_phone_numbers_from_text - Find all phone numbers in text
    • extract_valid_phone_numbers_from_text - Find only valid numbers
    • extract_phone_numbers_with_country_hint - Parse with default country
    • replace_phone_numbers_in_text - Custom replacement function
    • redact_phone_numbers - Privacy-focused masking
    • count_phone_numbers_in_text - Quick count
  • โš–๏ธ PhoneNumber Struct - Type-safe phone numbers with equality
    • Implements Eq, PartialEq, Hash for use in collections
    • Implements Display, FromStr for easy conversion
    • Methods: e164(), national_number(), format(), is_mobile(), etc.
  • ๐Ÿ—‚๏ธ PhoneNumberSet - Efficient deduplication collection
  • ๐Ÿ“š Improved Documentation - Complete rustdoc coverage
  • ๐Ÿ”ง Code Quality - All clippy warnings resolved

v0.2.0

Major Feature Release

  • โœจ Phone Number Formatting - Multiple format support (E.164, International, National, RFC3966)
  • ๐Ÿ“ฑ Type Detection - Identify mobile, landline, toll-free, premium numbers
  • ๐ŸŽฒ Random Generation - Generate valid random phone numbers by country
  • โš–๏ธ Number Comparison - Compare numbers regardless of format
  • ๐Ÿš€ Batch Processing - Process multiple numbers efficiently
  • ๐Ÿ” Smart Suggestions - Get correction suggestions for invalid numbers
  • ๐Ÿ“Š Comprehensive Analysis - Detailed phone number analysis
  • ๐ŸŒ Enhanced Country Support - Better patterns for major countries

v0.1.6

  • ๐Ÿ› Bug fixes and performance improvements
  • ๐Ÿ“š Documentation updates

License

This project is licensed under the MIT License - see the LICENSE file for details.

Rusty Rails Project

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.

  • ๐Ÿ”— More Rust libraries coming soon!
  • ๐Ÿš€ Performance-focused Ruby alternatives
  • ๐Ÿ“ฆ Easy-to-use APIs familiar to Ruby developers

Made with โค๏ธ by the Rusty Rails team

No runtime deps