aibot 1.0.1

WeChat iLink Bot API — full async Rust implementation
Documentation
//! # 错误类型体系
//!
//! 用结构化的 [`BotError`] 枚举替代 `anyhow`,覆盖所有预期的失败场景:
//!
//! - **网络层**:HTTP 请求/传输失败
//! - **API 层**:服务端返回非零 `ret` 码
//! - **加解密**:AES 密钥解析、PKCS7 padding 校验等
//! - **I/O**:文件读写、Token 持久化
//! - **数据格式**:Base64/Hex 解码、JSON 结构异常
//! - **超时**:扫码登录超时等
//!
//! ## 用法
//!
//! ```rust
//! use wechat_ilink_bot::{BotError, BotResult};
//!
//! fn example() -> BotResult<()> {
//!     // 使用 ? 自动转换
//!     let _ = std::fs::read_to_string("foo.txt")?;
//!     Ok(())
//! }
//! ```

use std::fmt;
use xbw::all::AxiosError;
use xbw::crypt::{base64::DecodeError, hex};

/// 统一的 Bot 错误类型,覆盖所有操作失败模式。
#[derive(Debug)]
pub enum BotError {
    /// HTTP 请求/传输层错误(超时、连接拒绝、DNS 解析失败等)。
    Http(AxiosError),

    /// API 业务层错误 —— 服务端返回了非零的 `ret` 码。
    Api {
        /// 错误码(来自响应中的 `ret` 字段)。
        ret: i64,
        /// 错误描述。
        message: String,
    },

    /// AES 加解密相关错误(密钥解析失败、padding 校验失败等)。
    Crypto(String),

    /// I/O 操作错误(文件读写、Token 持久化等)。
    Io(std::io::Error),

    /// 数据格式错误(Base64 解码、Hex 解码、JSON 反序列化失败等)。
    InvalidData(String),

    /// 操作超时(扫码等待超时等)。
    Timeout(String),

    /// 其他未分类错误。
    Other(String),
}

impl fmt::Display for BotError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            BotError::Http(e) => write!(f, "HTTP 错误: {}", e),
            BotError::Api { ret, message } => {
                write!(f, "API 错误 (ret={}): {}", ret, message)
            }
            BotError::Crypto(msg) => write!(f, "加解密错误: {}", msg),
            BotError::Io(e) => write!(f, "I/O 错误: {}", e),
            BotError::InvalidData(msg) => write!(f, "数据格式错误: {}", msg),
            BotError::Timeout(msg) => write!(f, "超时: {}", msg),
            BotError::Other(msg) => write!(f, "{}", msg),
        }
    }
}

impl std::error::Error for BotError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            BotError::Http(e) => Some(e),
            BotError::Io(e) => Some(e),
            _ => None,
        }
    }
}

// ── From 实现:让 `?` 运算符自动转换 ────────

impl From<AxiosError> for BotError {
    fn from(e: AxiosError) -> Self {
        BotError::Http(e)
    }
}

impl From<std::io::Error> for BotError {
    fn from(e: std::io::Error) -> Self {
        BotError::Io(e)
    }
}

impl From<DecodeError> for BotError {
    fn from(e: DecodeError) -> Self {
        BotError::InvalidData(format!("Base64 解码失败: {}", e))
    }
}

impl From<hex::FromHexError> for BotError {
    fn from(e: hex::FromHexError) -> Self {
        BotError::InvalidData(format!("Hex 解码失败: {}", e))
    }
}

// ── 便捷构造器 ──────────────────────────────

impl BotError {
    /// 创建一个"其他"类型的错误。
    pub fn other(msg: impl Into<String>) -> Self {
        BotError::Other(msg.into())
    }

    /// 创建一个加解密错误。
    pub fn crypto(msg: impl Into<String>) -> Self {
        BotError::Crypto(msg.into())
    }

    /// 创建一个 API 业务错误。
    pub fn api(ret: i64, message: impl Into<String>) -> Self {
        BotError::Api {
            ret,
            message: message.into(),
        }
    }

    /// 创建一个超时错误。
    pub fn timeout(msg: impl Into<String>) -> Self {
        BotError::Timeout(msg.into())
    }

    /// 创建一个数据格式错误。
    pub fn invalid_data(msg: impl Into<String>) -> Self {
        BotError::InvalidData(msg.into())
    }
}

/// [`BotError`] 的 Result 别名,免去每次写 `Result<T, BotError>` 的冗长。
///
/// # 示例
///
/// ```rust
/// use wechat_ilink_bot::BotResult;
///
/// fn example() -> BotResult<()> {
///     Ok(())
/// }
/// ```
pub type BotResult<T> = Result<T, BotError>;