kcode-tg-kennedy-bot 0.2.0

A host-integrated Telegram transport, durable queue, and fail-closed group-security library
Documentation
use teloxide::{DownloadError, RequestError};

pub(super) fn request_error_class(error: &RequestError) -> &'static str {
    match error {
        RequestError::Api(_) => "telegram_api",
        RequestError::MigrateToChatId(_) => "telegram_migrate",
        RequestError::RetryAfter(_) => "telegram_rate_limit",
        RequestError::Network(network) if network.is_timeout() => "telegram_network_timeout",
        RequestError::Network(network) if network.is_connect() => "telegram_network_connect",
        RequestError::Network(_) => "telegram_network",
        RequestError::InvalidJson { .. } => "telegram_invalid_json",
        RequestError::Io(error) => io_error_class(error.kind()),
    }
}

fn download_error_class(error: &DownloadError) -> &'static str {
    match error {
        DownloadError::Network(network) if network.is_timeout() => {
            "telegram_download_network_timeout"
        }
        DownloadError::Network(network) if network.is_connect() => {
            "telegram_download_network_connect"
        }
        DownloadError::Network(_) => "telegram_download_network",
        DownloadError::Io(error) => io_error_class(error.kind()),
    }
}

fn io_error_class(kind: std::io::ErrorKind) -> &'static str {
    match kind {
        std::io::ErrorKind::TimedOut => "io_timeout",
        std::io::ErrorKind::ConnectionRefused => "io_connection_refused",
        std::io::ErrorKind::ConnectionReset => "io_connection_reset",
        std::io::ErrorKind::ConnectionAborted => "io_connection_aborted",
        std::io::ErrorKind::NotConnected => "io_not_connected",
        std::io::ErrorKind::BrokenPipe => "io_broken_pipe",
        std::io::ErrorKind::UnexpectedEof => "io_unexpected_eof",
        std::io::ErrorKind::PermissionDenied => "io_permission_denied",
        std::io::ErrorKind::NotFound => "io_not_found",
        _ => "io_other",
    }
}

pub(super) fn anyhow_error_class(error: &anyhow::Error) -> &'static str {
    for cause in error.chain() {
        if let Some(request_error) = cause.downcast_ref::<RequestError>() {
            return request_error_class(request_error);
        }
        if let Some(download_error) = cause.downcast_ref::<DownloadError>() {
            return download_error_class(download_error);
        }
        if let Some(sqlite_error) = cause.downcast_ref::<rusqlite::Error>() {
            return match sqlite_error {
                rusqlite::Error::SqliteFailure(_, _) => "sqlite_failure",
                rusqlite::Error::QueryReturnedNoRows => "sqlite_not_found",
                rusqlite::Error::InvalidQuery => "sqlite_invalid_query",
                _ => "sqlite_other",
            };
        }
        if let Some(io_error) = cause.downcast_ref::<std::io::Error>() {
            return io_error_class(io_error.kind());
        }
    }
    "local_processing"
}