ai-agent 0.13.4

Idiomatic agent sdk inspired by the claude code source leak
Documentation
//! API error utilities - translated from errorUtils.ts

use serde::{Deserialize, Serialize};

/// Extract connection error details from an error
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ConnectionErrorDetails {
    pub is_ssl_error: bool,
    pub is_timeout: bool,
    pub is_connection_refused: bool,
    pub errno: Option<String>,
}

/// Extract connection error details from an error message
pub fn extract_connection_error_details(error: &str) -> Option<ConnectionErrorDetails> {
    let lower = error.to_lowercase();

    Some(ConnectionErrorDetails {
        is_ssl_error: lower.contains("ssl")
            || lower.contains("tls")
            || lower.contains("certificate"),
        is_timeout: lower.contains("timeout") || lower.contains("timed out"),
        is_connection_refused: lower.contains("connection refused")
            || lower.contains("econnrefused"),
        errno: None, // Would need more context to extract
    })
}

/// Format an API error for display
pub fn format_api_error(error: &str) -> String {
    let lower = error.to_lowercase();

    if lower.contains("timeout") || lower.contains("timed out") {
        return "Connection timeout. Please check your network and try again.".to_string();
    }

    if lower.contains("certificate") || lower.contains("ssl") || lower.contains("tls") {
        return "SSL/TLS certificate error. Please check your system date and network.".to_string();
    }

    if lower.contains("connection refused") || lower.contains("econnrefused") {
        return "Connection refused. The server may be down or unreachable.".to_string();
    }

    if lower.contains("dns")
        || lower.contains("getaddrinfo")
        || lower.contains("ename or service not known")
    {
        return "DNS error. Please check your internet connection.".to_string();
    }

    // Generic fallback
    "Connection error. Please check your network and try again.".to_string()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_extract_connection_error_details_timeout() {
        let details = extract_connection_error_details("Connection timeout").unwrap();
        assert!(details.is_timeout);
        assert!(!details.is_ssl_error);
    }

    #[test]
    fn test_extract_connection_error_details_ssl() {
        let details = extract_connection_error_details("SSL certificate error").unwrap();
        assert!(details.is_ssl_error);
        assert!(!details.is_timeout);
    }

    #[test]
    fn test_format_api_error_timeout() {
        let msg = format_api_error("Connection timed out");
        assert!(msg.contains("timeout"));
    }

    #[test]
    fn test_format_api_error_ssl() {
        let msg = format_api_error("SSL certificate problem");
        assert!(msg.contains("SSL") || msg.contains("certificate"));
    }
}