Rust错误处理库ic-error-types的使用:高效管理IC(Internet Computer)生态系统的自定义错误类型

Rust错误处理库ic-error-types的使用:高效管理IC(Internet Computer)生态系统的自定义错误类型

ic-error-types 是一个 Rust 库,包含了 Internet Computer (IC) 生态系统中定义的 RejectCode 错误代码,这些代码在 IC 接口规范中有详细说明,同时也包含了一些未在规范中定义的实现特定的错误代码。

安装

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

cargo add ic-error-types

或者在你的 Cargo.toml 文件中添加以下行:

ic-error-types = "0.2.0"

使用示例

下面是一个完整的使用示例,展示如何在你的 IC 项目中处理自定义错误类型:

use ic_error_types::{RejectCode, UserError};
use std::convert::TryFrom;

fn main() {
    // 创建一个系统错误
    let system_error = RejectCode::SysFatal;
    
    // 将RejectCode转换为u64
    let error_code: u64 = system_error as u64;
    println!("System error code: {}", error_code);
    
    // 从u64尝试转换回RejectCode
    match RejectCode::try_from(5) {
        Ok(code) => println!("Converted back to RejectCode: {:?}", code),
        Err(_) => println!("Invalid error code"),
    }
    
    // 创建用户错误
    let user_error = UserError::new(
        RejectCode::CanisterError,
        "Custom error message".to_string(),
    );
    
    // 处理用户错误
    match user_error.reject_code() {
        RejectCode::CanisterError => {
            println!("Handling canister error: {}", user_error.message());
        }
        _ => println!("Other type of error"),
    }
    
    // 使用IC规范中定义的错误代码
    let canister_reject = RejectCode::CanisterReject;
    println!("Canister reject code: {}", canister_reject as u64);
}

完整示例demo

下面是一个更完整的示例,展示了如何在IC canister开发中使用ic-error-types进行错误处理:

use ic_error_types::{RejectCode, UserError};
use std::convert::TryFrom;

// 模拟一个canister函数
fn process_request(input: u32) -> Result<u32, UserError> {
    if input == 0 {
        // 返回一个系统错误
        Err(UserError::new(
            RejectCode::SysTransient,
            "Input cannot be zero".to_string(),
        ))
    } else if input > 100 {
        // 返回一个canister错误
        Err(UserError::new(
            RejectCode::CanisterError,
            "Input exceeds maximum value".to_string(),
        ))
    } else {
        // 处理成功
        Ok(input * 2)
    }
}

fn main() {
    // 测试不同的输入情况
    let test_cases = vec![0, 150, 50];
    
    for input in test_cases {
        match process_request(input) {
            Ok(result) => println!("Success: {}", result),
            Err(err) => {
                // 根据错误类型进行不同处理
                match err.reject_code() {
                    RejectCode::SysTransient => {
                        println!("System transient error: {}", err.message());
                        // 可以在这里添加重试逻辑
                    }
                    RejectCode::CanisterError => {
                        println!("Canister error: {}", err.message());
                        // 可以在这里记录错误或通知管理员
                    }
                    _ => println!("Unexpected error: {:?}", err),
                }
                
                // 打印错误代码
                println!("Error code: {}", err.reject_code() as u64);
            }
        }
    }
    
    // 演示错误代码转换
    println!("\nError code conversion examples:");
    for code in 0..=6 {
        match RejectCode::try_from(code) {
            Ok(reject_code) => println!("Code {}: {:?}", code, reject_code),
            Err(_) => println!("Code {}: Invalid", code),
        }
    }
    
    // 使用IC规范中定义的错误代码
    let important_error = RejectCode::CanisterReject;
    println!("\nImportant error code: {}", important_error as u64);
}

示例代码说明

  1. 定义了一个process_request函数模拟canister中的请求处理
  2. 根据不同的输入返回不同的错误类型:
    • 输入为0时返回系统临时错误
    • 输入超过100时返回canister错误
    • 正常情况下返回处理结果
  3. 在主函数中测试了不同的输入情况
  4. 展示了如何根据不同的错误类型采取不同的处理措施
  5. 演示了错误代码与枚举值之间的转换
  6. 使用了IC规范中定义的特定错误代码

主要功能

ic-error-types 提供以下主要功能:

  1. 标准化的 IC 错误代码枚举 (RejectCode)
  2. 用户错误类型 (UserError) 用于创建自定义错误
  3. 错误代码与数字之间的转换
  4. 支持 IC 规范中定义的所有标准错误代码
  5. 一些实现特定的额外错误代码

这个库由 dfinity/execution 团队维护,是 Internet Computer 生态系统的重要组成部分,特别适合在 IC 上的 canister 开发中使用。


1 回复

Rust错误处理库 ic-error-types 的使用:高效管理IC生态系统的自定义错误类型

ic-error-types 是一个专门为 Internet Computer (IC) 生态系统设计的 Rust 错误处理库,它简化了在 IC 上开发时自定义错误类型的创建和管理。

主要特性

  • 专为 IC 生态优化的错误处理
  • 简化自定义错误类型的定义
  • 提供与 IC 系统良好集成的错误转换
  • 支持清晰的错误分类和层次结构

安装

Cargo.toml 中添加依赖:

[dependencies]
ic-error-types = "0.1"

完整示例代码

// 引入必要的库
use ic_error_types::{ErrorType, ErrorCode, ResultExt, ToCanisterError};
use ic_cdk::api;

// 定义数据库错误类型
#[derive(Debug, ErrorType)]
pub enum DatabaseError {
    #[error("Connection failed")]
    ConnectionFailed,
    #[error("Query failed: {0}")]
    QueryFailed(String),
}

// 实现错误代码
impl ErrorCode for DatabaseError {
    fn code(&self) -> u32 {
        match self {
            DatabaseError::ConnectionFailed => 5001,
            DatabaseError::QueryFailed(_) => 5002,
        }
    }
}

// 定义业务逻辑错误类型
#[derive(Debug, ErrorType)]
pub enum BusinessError {
    #[error("User not found")]
    UserNotFound,
    #[error("Invalid permission")]
    InvalidPermission,
    #[error("Database error: {0}")]
    DatabaseError(#[from] DatabaseError),
}

// 实现错误代码
impl ErrorCode for BusinessError {
    fn code(&self) -> u32 {
        match self {
            BusinessError::UserNotFound => 4001,
            BusinessError::InvalidPermission => 4003,
            BusinessError::DatabaseError(e) => e.code(),
        }
    }
}

// 模拟数据库操作函数
fn db_query(user_id: u64) -> Result<String, DatabaseError> {
    if user_id == 0 {
        Err(DatabaseError::QueryFailed("Invalid user ID".to_string()))
    } else {
        Ok(format!("User data for {}", user_id))
    }
}

// 业务逻辑函数
fn get_user_data(user_id: u64, is_admin: bool) -> Result<String, BusinessError> {
    if !is_admin {
        return Err(BusinessError::InvalidPermission);
    }
    
    let data = db_query(user_id)?; // 使用?自动转换错误
    
    if data.contains("no data") {
        Err(BusinessError::UserNotFound)
    } else {
        Ok(data)
    }
}

// IC Canister 入口函数
#[ic_cdk::update]
fn canister_get_user_data(user_id: u64, is_admin: bool) -> Result<String, String> {
    get_user_data(user_id, is_admin)
        .to_canister_error() // 转换为IC canister兼容的错误格式
}

// 测试用例
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_permission_error() {
        let result = get_user_data(1, false);
        assert!(matches!(result, Err(BusinessError::InvalidPermission)));
    }

    #[test]
    fn test_database_error_propagation() {
        let result = get_user_data(0, true);
        assert!(matches!(
            result, 
            Err(BusinessError::DatabaseError(DatabaseError::QueryFailed(_)))
        ));
    }
}

代码说明

  1. 错误类型定义

    • 定义了DatabaseErrorBusinessError两种错误类型
    • 使用#[derive(ErrorType)]自动实现常见trait
    • 实现了ErrorCode trait为每种错误指定唯一代码
  2. 错误嵌套

    • BusinessError包含了DatabaseError作为嵌套错误
    • 使用#[from]属性自动实现From trait
  3. IC集成

    • to_canister_error()方法将自定义错误转换为IC兼容格式
    • Canister入口函数返回Result<String, String>类型
  4. 测试用例

    • 验证权限错误处理
    • 验证数据库错误传播

这个完整示例展示了如何在IC生态系统中使用ic-error-types进行全面的错误处理,包括错误定义、嵌套、转换以及与IC系统的集成。

回到顶部