Skip to main content

vtcode_llm/
error_display.rs

1//! LLM error display utilities with enhanced ANSI color support
2//!
3//! This module provides enhanced error display capabilities for LLM providers
4//! using standard console styling for consistent terminal output.
5
6use anstyle::Style;
7use vtcode_commons::color_policy;
8use vtcode_commons::sanitizer::sanitize_provider_diagnostic;
9use vtcode_commons::styling::Styles;
10
11/// Internal helper to wrap text with style codes - reduces duplication
12#[inline]
13fn style_text(style: Style, text: &str) -> String {
14    if !color_policy::color_output_enabled() {
15        return text.to_string();
16    }
17    format!("{}{}{}", Styles::render(&style), text, Styles::render_reset())
18}
19
20/// Get a styled error message with enhanced coloring
21#[cold]
22fn style_llm_error(message: &str) -> String {
23    style_text(Styles::error(), message)
24}
25
26/// Get a styled warning message with enhanced coloring
27#[cold]
28fn style_llm_warning(message: &str) -> String {
29    style_text(Styles::warning(), message)
30}
31
32/// Get a styled success message with enhanced coloring
33#[inline]
34fn style_llm_success(message: &str) -> String {
35    style_text(Styles::success(), message)
36}
37
38/// Get a styled provider name with enhanced coloring based on provider type
39fn style_provider_name(provider: &str) -> String {
40    let style = match provider.to_lowercase().as_str() {
41        "gemini" => Styles::info(),    // Deep blue for Gemini
42        "openai" => Styles::warning(), // Bright orange for OpenAI
43        "anthropic" => Styles::code(), // Anthropic's brand purple
44        _ => Styles::debug(),          // Default styling for other providers
45    };
46    style_text(style, provider)
47}
48
49/// Format an LLM error for display with enhanced coloring
50#[cold]
51pub fn format_llm_error(provider: &str, error: &str) -> String {
52    let provider_styled = style_provider_name(provider);
53    let safe_error = sanitize_provider_diagnostic(error.as_bytes());
54    let error_styled = style_llm_error(&safe_error);
55    format!("{provider_styled} {error_styled}")
56}
57
58/// Format an LLM warning for display with enhanced coloring
59#[cold]
60fn format_llm_warning(provider: &str, warning: &str) -> String {
61    let provider_styled = style_provider_name(provider);
62    let warning_styled = style_llm_warning(warning);
63    format!("{provider_styled} {warning_styled}")
64}
65
66/// Format an LLM success message for display with enhanced coloring
67fn format_llm_success(provider: &str, message: &str) -> String {
68    let provider_styled = style_provider_name(provider);
69    let success_styled = style_llm_success(message);
70    format!("{provider_styled} {success_styled}")
71}
72
73/// Format a network error for display with enhanced coloring.
74/// This is a convenience wrapper for the common "Network error: {}" pattern.
75#[cold]
76pub fn format_network_error(provider: &str, error: &impl std::fmt::Display) -> String {
77    format_llm_error(provider, &format!("Network error: {error}"))
78}
79
80/// Format a parse error for display with enhanced coloring.
81/// This is a convenience wrapper for the common "Parse error: {}" pattern.
82#[cold]
83pub fn format_parse_error(provider: &str, error: &impl std::fmt::Display) -> String {
84    format_llm_error(provider, &format!("Parse error: {error}"))
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn test_style_llm_error() {
93        let result = style_llm_error("Test error");
94        assert!(!result.is_empty());
95    }
96
97    #[test]
98    fn test_style_llm_warning() {
99        let result = style_llm_warning("Test warning");
100        assert!(!result.is_empty());
101    }
102
103    #[test]
104    fn test_style_llm_success() {
105        let result = style_llm_success("Test success");
106        assert!(!result.is_empty());
107    }
108
109    #[test]
110    fn test_style_provider_name() {
111        let providers = vec!["gemini", "openai", "anthropic", "ollama", "lmstudio", "unknown"];
112        for provider in providers {
113            let result = style_provider_name(provider);
114            assert!(!result.is_empty());
115        }
116    }
117
118    #[test]
119    fn test_format_llm_error() {
120        let result = format_llm_error("gemini", "Connection failed");
121        assert!(result.contains("gemini"));
122        assert!(result.contains("Connection failed"));
123    }
124
125    #[test]
126    fn test_format_llm_warning() {
127        let result = format_llm_warning("openai", "Rate limit approaching");
128        assert!(result.contains("openai"));
129        assert!(result.contains("Rate limit approaching"));
130    }
131
132    #[test]
133    fn test_format_llm_success() {
134        let result = format_llm_success("anthropic", "Request completed");
135        assert!(result.contains("anthropic"));
136        assert!(result.contains("Request completed"));
137    }
138}