miyabi_telegram/
error.rs

1//! Error types for Telegram Bot API
2
3use miyabi_types::error::{ErrorCode, UnifiedError};
4use std::any::Any;
5use thiserror::Error;
6
7/// Telegram Bot API error types
8#[derive(Error, Debug)]
9pub enum TelegramError {
10    /// HTTP request failed
11    #[error("HTTP request failed: {0}")]
12    HttpError(#[from] reqwest::Error),
13
14    /// JSON serialization/deserialization failed
15    #[error("JSON error: {0}")]
16    JsonError(#[from] serde_json::Error),
17
18    /// Telegram API returned an error
19    #[error("Telegram API error: {0}")]
20    ApiError(String),
21
22    /// Invalid bot token
23    #[error("Invalid bot token")]
24    InvalidToken,
25
26    /// Missing environment variable
27    #[error("Missing environment variable: {0}")]
28    MissingEnvVar(String),
29
30    /// Other errors
31    #[error("Other error: {0}")]
32    Other(String),
33}
34
35/// Result type alias for Telegram operations
36pub type Result<T> = std::result::Result<T, TelegramError>;
37
38// ============================================================================
39// UnifiedError Implementation
40// ============================================================================
41
42impl UnifiedError for TelegramError {
43    fn code(&self) -> ErrorCode {
44        match self {
45            Self::HttpError(_) => ErrorCode::HTTP_ERROR,
46            Self::JsonError(_) => ErrorCode::PARSE_ERROR,
47            Self::ApiError(_) => ErrorCode::HTTP_ERROR,
48            Self::InvalidToken => ErrorCode::AUTH_ERROR,
49            Self::MissingEnvVar(_) => ErrorCode::MISSING_CONFIG,
50            Self::Other(_) => ErrorCode::INTERNAL_ERROR,
51        }
52    }
53
54    fn user_message(&self) -> String {
55        match self {
56            Self::ApiError(msg) => format!(
57                "Telegram API returned an error: {}. Please check your bot configuration and try again.",
58                msg
59            ),
60            Self::InvalidToken => {
61                "Invalid Telegram bot token. Please verify your bot token in the configuration.".to_string()
62            }
63            Self::MissingEnvVar(var) => format!(
64                "Missing environment variable: {}. Please set this variable in your .env file or environment.",
65                var
66            ),
67            Self::Other(msg) => format!(
68                "Telegram bot error: {}. Please check logs for more details.",
69                msg
70            ),
71            // Reuse existing thiserror messages for other variants
72            _ => self.to_string(),
73        }
74    }
75
76    fn context(&self) -> Option<&dyn Any> {
77        match self {
78            Self::ApiError(msg) => Some(msg as &dyn Any),
79            Self::MissingEnvVar(var) => Some(var as &dyn Any),
80            Self::Other(msg) => Some(msg as &dyn Any),
81            _ => None,
82        }
83    }
84}
85
86#[cfg(test)]
87mod unified_error_tests {
88    use super::*;
89
90    #[test]
91    fn test_telegram_error_codes() {
92        let error = TelegramError::ApiError("rate limit".to_string());
93        assert_eq!(error.code(), ErrorCode::HTTP_ERROR);
94
95        let error = TelegramError::InvalidToken;
96        assert_eq!(error.code(), ErrorCode::AUTH_ERROR);
97
98        let error = TelegramError::MissingEnvVar("TELEGRAM_BOT_TOKEN".to_string());
99        assert_eq!(error.code(), ErrorCode::MISSING_CONFIG);
100
101        let error = TelegramError::Other("unknown".to_string());
102        assert_eq!(error.code(), ErrorCode::INTERNAL_ERROR);
103    }
104
105    #[test]
106    fn test_user_messages() {
107        let error = TelegramError::ApiError("Bad Request: message too long".to_string());
108        let msg = error.user_message();
109        assert!(msg.contains("Telegram API"));
110        assert!(msg.contains("message too long"));
111
112        let error = TelegramError::InvalidToken;
113        let msg = error.user_message();
114        assert!(msg.contains("Invalid"));
115        assert!(msg.contains("bot token"));
116
117        let error = TelegramError::MissingEnvVar("TELEGRAM_CHAT_ID".to_string());
118        let msg = error.user_message();
119        assert!(msg.contains("Missing environment variable"));
120        assert!(msg.contains("TELEGRAM_CHAT_ID"));
121    }
122
123    #[test]
124    fn test_context_extraction() {
125        let error = TelegramError::ApiError("error".to_string());
126        assert!(error.context().is_some());
127
128        let error = TelegramError::MissingEnvVar("TOKEN".to_string());
129        assert!(error.context().is_some());
130
131        let error = TelegramError::InvalidToken;
132        assert!(error.context().is_none());
133
134        // Note: reqwest::Error cannot be constructed directly (private constructor)
135        // HttpError context test is implicitly covered by the InvalidToken test above
136        // as both return None from context()
137    }
138}