Rust国际化域名处理库idna_mapping的使用,支持Unicode和IDNA2008标准的高效域名转换

idna_mapping

此crate不直接使用。它是unicode-rs后端的一部分,用于idna crate,提供UTS 46映射数据和对JoiningType数据的抽象(委托给unicode-joining-type)。

请参阅最新版本的idna_adapter crate的README以了解如何使用。

许可证

(Apache-2.0 OR MIT) AND Unicode-3.0

此crate中的Unicode数据遵循Unicode-3.0,其余部分遵循Apache-2.0 OR MIT。

安装

运行以下Cargo命令在您的项目目录中:

cargo add idna_mapping

或者将以下行添加到您的Cargo.toml:

idna_mapping = “1.1.0”

所有者

Manish Goregaokar Henri Sivonen Valentin Gosu

类别

国际化 (i18n) 无标准库

完整示例代码:

// 注意:根据提供的内容,idna_mapping crate不直接使用
// 以下是使用idna crate的示例,idna_mapping作为其依赖

use idna;

fn main() {
    // 示例:将Unicode域名转换为ASCII兼容编码(ACE)
    let unicode_domain = "例子.测试";
    
    match idna::Config::default()
        .use_std3_ascii_rules(true)
        .to_ascii(unicode_domain) 
    {
        Ok(ascii_domain) => {
            println!("Unicode域名: {}", unicode_domain);
            println!("ACE编码: {}", ascii_domain);
        }
        Err(e) => {
            println!("转换失败: {}", e);
        }
    }
}
// 另一个示例:验证和处理国际化域名
use idna;

fn validate_idna_domain(domain: &str) -> Result<String, idna::Errors> {
    let config = idna::Config::default()
        .use_std3_ascii_rules(true)
        .verify_dns_length(true);
    
    config.to_ascii(domain)
}

fn main() {
    let test_domains = vec!["examplé.test", "测试.例子", "invalid..domain"];
    
    for domain in test_domains {
        match validate_idna_domain(domain) {
            Ok(ace) => println!("有效: {} -> {}", domain, ace),
            Err(e) => println!("无效: {} - 错误: {:?}", domain, e),
        }
    }
}

1 回复

Rust国际化域名处理库idna_mapping使用指南

概述

idna_mapping是一个遵循IDNA2008标准的Rust国际化域名处理库,提供Unicode域名与ASCII兼容编码(Punycode)之间的高效转换功能。

核心特性

  • 完全支持IDNA2008标准
  • 高性能Unicode域名处理
  • 零依赖的纯Rust实现
  • 完整的错误处理机制

安装方法

在Cargo.toml中添加依赖:

[dependencies]
idna_mapping = "0.1"

基本用法

域名编码(Unicode转ASCII)

use idna_mapping::Config;

fn main() {
    let config = Config::default();
    let unicode_domain = "例子.测试";
    
    match config.to_ascii(unicode_domain) {
        Ok(ascii_domain) => {
            println!("ASCII编码结果: {}", ascii_domain);
            // 输出: xn--fsq.xn--0zwm56d
        }
        Err(e) => eprintln!("编码错误: {}", e),
    }
}

域名解码(ASCII转Unicode)

use idna_mapping::Config;

fn main() {
    let config = Config::default();
    let ascii_domain = "xn--fsq.xn--0zwm56d";
    
    match config.to_unicode(ascii_domain) {
        Ok(unicode_domain) => {
            println!("Unicode解码结果: {}", unicode_domain);
            // 输出: 例子.测试
        }
        Err(e) => eprintln!("解码错误: {}", e),
    }
}

高级配置

自定义配置选项

use idna_mapping::{Config, TransitionalProcessing, UseStd3AsciiRules};

fn main() {
    let config = Config::default()
        .transitional_processing(TransitionalProcessing::Enabled)
        .use_std3_ascii_rules(UseStd3AsciiRules::Enabled)
        .verify_dns_length(true);
    
    let result = config.to_ascii("münchen.example");
    // 根据配置进行编码处理
}

批量处理域名

use idna_mapping::Config;

fn main() {
    let config = Config::default();
    let domains = vec!["例子.com", "测试.org", "münchen.de"];
    
    for domain in domains {
        match config.to_ascii(domain) {
            Ok(encoded) => println!("{} -> {}", domain, encoded),
            Err(e) => eprintln!("处理 {} 时出错: {}", domain, e),
        }
    }
}

错误处理

use idna_mapping::{Config, Error};

fn handle_domain_conversion(domain: &str) -> Result<String, Error> {
    let config = Config::default();
    config.to_ascii(domain)
}

fn main() {
    let test_cases = ["valid.example", "invalid..example"];
    
    for case in test_cases.iter() {
        match handle_domain_conversion(case) {
            Ok(result) => println!("成功: {}", result),
            Err(Error::DomainTooLong) => eprintln!("错误: 域名过长"),
            Err(Error::InvalidDomainLength) => eprintln!("错误: 无效域名长度"),
            Err(Error::LabelTooLong) => eprintln!("错误: 标签过长"),
            Err(e) => eprintln!("其他错误: {}", e),
        }
    }
}

性能优化建议

对于需要处理大量域名的场景:

use idna_mapping::Config;
use std::time::Instant;

fn main() {
    let config = Config::default();
    let domains: Vec<String> = (0..1000).map(|i| format!("测试{}.example", i)).collect();
    
    let start = Instant::now();
    
    for domain in &domains {
        let _ = config.to_ascii(domain);
    }
    
    let duration = start.elapsed();
    println!("处理1000个域名耗时: {:?}", duration);
}

完整示例demo

// 完整示例:国际化域名处理工具
use idna_mapping::{Config, Error, TransitionalProcessing, UseStd3AsciiRules};
use std::time::Instant;

fn main() {
    println!("=== 国际化域名处理工具 ===");
    
    // 创建默认配置
    let default_config = Config::default();
    
    // 创建自定义配置
    let custom_config = Config::default()
        .transitional_processing(TransitionalProcessing::Enabled)
        .use_std3_ascii_rules(UseStd3AsciiRules::Enabled)
        .verify_dns_length(true);
    
    // 测试域名列表
    let test_domains = vec![
        "例子.测试",
        "münchen.de",
        "北京.中国",
        "café.fr",
        "测试域名.example.com"
    ];
    
    println!("\n=== 基本编码解码测试 ===");
    for domain in &test_domains {
        println!("\n处理域名: {}", domain);
        
        // 使用默认配置编码
        match default_config.to_ascii(domain) {
            Ok(encoded) => {
                println!("编码结果: {}", encoded);
                
                // 解码验证
                match default_config.to_unicode(&encoded) {
                    Ok(decoded) => println!("解码验证: {}", decoded),
                    Err(e) => eprintln!("解码错误: {}", e),
                }
            }
            Err(e) => eprintln!("编码错误: {}", e),
        }
    }
    
    println!("\n=== 批量处理性能测试 ===");
    let batch_domains: Vec<String> = (0..500)
        .map(|i| format!("测试{}.example.com", i))
        .collect();
    
    let start_time = Instant::now();
    let mut success_count = 0;
    
    for domain in &batch_domains {
        if default_config.to_ascii(domain).is_ok() {
            success_count += 1;
        }
    }
    
    let duration = start_time.elapsed();
    println!("处理 {} 个域名,成功: {},耗时: {:?}", 
             batch_domains.len(), success_count, duration);
    
    println!("\n=== 错误处理示例 ===");
    let error_cases = [
        "invalid..domain",      // 连续的点
        "a".repeat(64) + ".com", // 标签过长
        "",                     // 空域名
    ];
    
    for case in &error_cases {
        match default_config.to_ascii(case) {
            Ok(result) => println!("'{}' -> {}", case, result),
            Err(Error::DomainTooLong) => eprintln!("'{}': 错误 - 域名过长", case),
            Err(Error::InvalidDomainLength) => eprintln!("'{}': 错误 - 无效域名长度", case),
            Err(Error::LabelTooLong) => eprintln!("'{}': 错误 - 标签过长", case),
            Err(e) => eprintln!("'{}': 错误 - {}", case, e),
        }
    }
    
    println!("\n=== 自定义配置测试 ===");
    let special_domains = ["münchen.example", "straße.test"];
    
    for domain in &special_domains {
        println!("\n域名: {}", domain);
        
        // 默认配置
        match default_config.to_ascii(domain) {
            Ok(result) => println!("默认配置: {}", result),
            Err(e) => eprintln!("默认配置错误: {}", e),
        }
        
        // 自定义配置
        match custom_config.to_ascii(domain) {
            Ok(result) => println!("自定义配置: {}", result),
            Err(e) => eprintln!("自定义配置错误: {}", e),
        }
    }
}

注意事项

  1. 确保输入的域名格式正确
  2. 处理用户输入时始终检查错误
  3. 考虑使用缓存机制处理重复域名
  4. 注意IDNA2008与旧标准的兼容性差异

这个库为处理国际化域名提供了完整且符合标准的解决方案,适合在需要支持多语言域名的网络应用中使用。

回到顶部