sql_cli/sql/functions/
ansi.rs

1use crate::data::datatable::DataValue;
2use crate::sql::functions::{ArgCount, FunctionCategory, FunctionSignature, SqlFunction};
3use anyhow::{anyhow, Result};
4
5/// ANSI color codes
6const ANSI_RESET: &str = "\x1b[0m";
7
8/// Named color mappings
9fn get_color_code(color_name: &str) -> Option<&'static str> {
10    match color_name.to_lowercase().as_str() {
11        // Foreground colors (30-37)
12        "black" => Some("\x1b[30m"),
13        "red" => Some("\x1b[31m"),
14        "green" => Some("\x1b[32m"),
15        "yellow" => Some("\x1b[33m"),
16        "blue" => Some("\x1b[34m"),
17        "magenta" | "purple" => Some("\x1b[35m"),
18        "cyan" => Some("\x1b[36m"),
19        "white" => Some("\x1b[37m"),
20        // Bright foreground colors (90-97)
21        "bright_black" | "gray" | "grey" => Some("\x1b[90m"),
22        "bright_red" => Some("\x1b[91m"),
23        "bright_green" => Some("\x1b[92m"),
24        "bright_yellow" => Some("\x1b[93m"),
25        "bright_blue" => Some("\x1b[94m"),
26        "bright_magenta" | "bright_purple" => Some("\x1b[95m"),
27        "bright_cyan" => Some("\x1b[96m"),
28        "bright_white" => Some("\x1b[97m"),
29        _ => None,
30    }
31}
32
33/// Background color mappings
34fn get_bg_color_code(color_name: &str) -> Option<&'static str> {
35    match color_name.to_lowercase().as_str() {
36        // Background colors (40-47)
37        "black" => Some("\x1b[40m"),
38        "red" => Some("\x1b[41m"),
39        "green" => Some("\x1b[42m"),
40        "yellow" => Some("\x1b[43m"),
41        "blue" => Some("\x1b[44m"),
42        "magenta" | "purple" => Some("\x1b[45m"),
43        "cyan" => Some("\x1b[46m"),
44        "white" => Some("\x1b[47m"),
45        // Bright background colors (100-107)
46        "bright_black" | "gray" | "grey" => Some("\x1b[100m"),
47        "bright_red" => Some("\x1b[101m"),
48        "bright_green" => Some("\x1b[102m"),
49        "bright_yellow" => Some("\x1b[103m"),
50        "bright_blue" => Some("\x1b[104m"),
51        "bright_magenta" | "bright_purple" => Some("\x1b[105m"),
52        "bright_cyan" => Some("\x1b[106m"),
53        "bright_white" => Some("\x1b[107m"),
54        _ => None,
55    }
56}
57
58/// ANSI_COLOR(color_name, text) - Apply foreground color to text
59pub struct AnsiColorFunction;
60
61impl SqlFunction for AnsiColorFunction {
62    fn signature(&self) -> FunctionSignature {
63        FunctionSignature {
64            name: "ANSI_COLOR",
65            category: FunctionCategory::Terminal,
66            arg_count: ArgCount::Fixed(2),
67            description: "Apply ANSI foreground color to text",
68            returns: "Colored text with ANSI escape codes",
69            examples: vec![
70                "SELECT ANSI_COLOR('red', 'ERROR')",
71                "SELECT ANSI_COLOR('green', 'SUCCESS')",
72                "SELECT ANSI_COLOR('yellow', amount) FROM sales",
73                "SELECT ANSI_COLOR('bright_blue', order_id) FROM orders",
74            ],
75        }
76    }
77
78    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
79        self.validate_args(args)?;
80
81        let color_name = match &args[0] {
82            DataValue::String(s) => s.as_str(),
83            DataValue::InternedString(s) => s.as_str(),
84            DataValue::Null => return Ok(args[1].clone()),
85            _ => return Err(anyhow!("ANSI_COLOR: first argument must be a color name")),
86        };
87
88        let text = match &args[1] {
89            DataValue::String(s) => s.clone(),
90            DataValue::InternedString(s) => s.to_string(),
91            DataValue::Integer(n) => n.to_string(),
92            DataValue::Float(f) => f.to_string(),
93            DataValue::Null => return Ok(DataValue::Null),
94            _ => args[1].to_string(),
95        };
96
97        let color_code = get_color_code(color_name).ok_or_else(|| {
98            anyhow!(
99                "ANSI_COLOR: unknown color '{}'. Available: black, red, green, yellow, blue, magenta, cyan, white, bright_* variants",
100                color_name
101            )
102        })?;
103
104        Ok(DataValue::String(format!(
105            "{}{}{}",
106            color_code, text, ANSI_RESET
107        )))
108    }
109}
110
111/// ANSI_BG(color_name, text) - Apply background color to text
112pub struct AnsiBgFunction;
113
114impl SqlFunction for AnsiBgFunction {
115    fn signature(&self) -> FunctionSignature {
116        FunctionSignature {
117            name: "ANSI_BG",
118            category: FunctionCategory::Terminal,
119            arg_count: ArgCount::Fixed(2),
120            description: "Apply ANSI background color to text",
121            returns: "Text with colored background using ANSI escape codes",
122            examples: vec![
123                "SELECT ANSI_BG('red', 'CRITICAL')",
124                "SELECT ANSI_BG('green', 'ACTIVE')",
125                "SELECT ANSI_BG('yellow', 'WARNING')",
126            ],
127        }
128    }
129
130    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
131        self.validate_args(args)?;
132
133        let color_name = match &args[0] {
134            DataValue::String(s) => s.as_str(),
135            DataValue::InternedString(s) => s.as_str(),
136            DataValue::Null => return Ok(args[1].clone()),
137            _ => return Err(anyhow!("ANSI_BG: first argument must be a color name")),
138        };
139
140        let text = match &args[1] {
141            DataValue::String(s) => s.clone(),
142            DataValue::InternedString(s) => s.to_string(),
143            DataValue::Integer(n) => n.to_string(),
144            DataValue::Float(f) => f.to_string(),
145            DataValue::Null => return Ok(DataValue::Null),
146            _ => args[1].to_string(),
147        };
148
149        let color_code = get_bg_color_code(color_name).ok_or_else(|| {
150            anyhow!(
151                "ANSI_BG: unknown color '{}'. Available: black, red, green, yellow, blue, magenta, cyan, white, bright_* variants",
152                color_name
153            )
154        })?;
155
156        Ok(DataValue::String(format!(
157            "{}{}{}",
158            color_code, text, ANSI_RESET
159        )))
160    }
161}
162
163/// ANSI_RGB(r, g, b, text) - Apply RGB foreground color to text
164pub struct AnsiRgbFunction;
165
166impl SqlFunction for AnsiRgbFunction {
167    fn signature(&self) -> FunctionSignature {
168        FunctionSignature {
169            name: "ANSI_RGB",
170            category: FunctionCategory::Terminal,
171            arg_count: ArgCount::Fixed(4),
172            description: "Apply RGB foreground color to text (0-255 for each component)",
173            returns: "Text colored with RGB value using ANSI escape codes",
174            examples: vec![
175                "SELECT ANSI_RGB(255, 0, 0, 'Bright Red')",
176                "SELECT ANSI_RGB(0, 255, 0, 'Bright Green')",
177                "SELECT ANSI_RGB(255, 165, 0, 'Orange')",
178                "SELECT ANSI_RGB(138, 43, 226, 'Blue Violet')",
179            ],
180        }
181    }
182
183    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
184        self.validate_args(args)?;
185
186        let r = match &args[0] {
187            DataValue::Integer(n) => {
188                if *n < 0 || *n > 255 {
189                    return Err(anyhow!("ANSI_RGB: red value must be 0-255, got {}", n));
190                }
191                *n as u8
192            }
193            _ => return Err(anyhow!("ANSI_RGB: red value must be an integer")),
194        };
195
196        let g = match &args[1] {
197            DataValue::Integer(n) => {
198                if *n < 0 || *n > 255 {
199                    return Err(anyhow!("ANSI_RGB: green value must be 0-255, got {}", n));
200                }
201                *n as u8
202            }
203            _ => return Err(anyhow!("ANSI_RGB: green value must be an integer")),
204        };
205
206        let b = match &args[2] {
207            DataValue::Integer(n) => {
208                if *n < 0 || *n > 255 {
209                    return Err(anyhow!("ANSI_RGB: blue value must be 0-255, got {}", n));
210                }
211                *n as u8
212            }
213            _ => return Err(anyhow!("ANSI_RGB: blue value must be an integer")),
214        };
215
216        let text = match &args[3] {
217            DataValue::String(s) => s.clone(),
218            DataValue::InternedString(s) => s.to_string(),
219            DataValue::Integer(n) => n.to_string(),
220            DataValue::Float(f) => f.to_string(),
221            DataValue::Null => return Ok(DataValue::Null),
222            _ => args[3].to_string(),
223        };
224
225        // ANSI 24-bit true color: ESC[38;2;r;g;bm
226        Ok(DataValue::String(format!(
227            "\x1b[38;2;{};{};{}m{}{}",
228            r, g, b, text, ANSI_RESET
229        )))
230    }
231}
232
233/// ANSI_RGB_BG(r, g, b, text) - Apply RGB background color to text
234pub struct AnsiRgbBgFunction;
235
236impl SqlFunction for AnsiRgbBgFunction {
237    fn signature(&self) -> FunctionSignature {
238        FunctionSignature {
239            name: "ANSI_RGB_BG",
240            category: FunctionCategory::Terminal,
241            arg_count: ArgCount::Fixed(4),
242            description: "Apply RGB background color to text (0-255 for each component)",
243            returns: "Text with RGB background using ANSI escape codes",
244            examples: vec![
245                "SELECT ANSI_RGB_BG(255, 0, 0, 'Red Background')",
246                "SELECT ANSI_RGB_BG(0, 128, 0, 'Dark Green BG')",
247            ],
248        }
249    }
250
251    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
252        self.validate_args(args)?;
253
254        let r = match &args[0] {
255            DataValue::Integer(n) => {
256                if *n < 0 || *n > 255 {
257                    return Err(anyhow!("ANSI_RGB_BG: red value must be 0-255, got {}", n));
258                }
259                *n as u8
260            }
261            _ => return Err(anyhow!("ANSI_RGB_BG: red value must be an integer")),
262        };
263
264        let g = match &args[1] {
265            DataValue::Integer(n) => {
266                if *n < 0 || *n > 255 {
267                    return Err(anyhow!("ANSI_RGB_BG: green value must be 0-255, got {}", n));
268                }
269                *n as u8
270            }
271            _ => return Err(anyhow!("ANSI_RGB_BG: green value must be an integer")),
272        };
273
274        let b = match &args[2] {
275            DataValue::Integer(n) => {
276                if *n < 0 || *n > 255 {
277                    return Err(anyhow!("ANSI_RGB_BG: blue value must be 0-255, got {}", n));
278                }
279                *n as u8
280            }
281            _ => return Err(anyhow!("ANSI_RGB_BG: blue value must be an integer")),
282        };
283
284        let text = match &args[3] {
285            DataValue::String(s) => s.clone(),
286            DataValue::InternedString(s) => s.to_string(),
287            DataValue::Integer(n) => n.to_string(),
288            DataValue::Float(f) => f.to_string(),
289            DataValue::Null => return Ok(DataValue::Null),
290            _ => args[3].to_string(),
291        };
292
293        // ANSI 24-bit true color background: ESC[48;2;r;g;bm
294        Ok(DataValue::String(format!(
295            "\x1b[48;2;{};{};{}m{}{}",
296            r, g, b, text, ANSI_RESET
297        )))
298    }
299}
300
301/// ANSI_BOLD(text) - Make text bold
302pub struct AnsiBoldFunction;
303
304impl SqlFunction for AnsiBoldFunction {
305    fn signature(&self) -> FunctionSignature {
306        FunctionSignature {
307            name: "ANSI_BOLD",
308            category: FunctionCategory::Terminal,
309            arg_count: ArgCount::Fixed(1),
310            description: "Make text bold using ANSI escape codes",
311            returns: "Bold text",
312            examples: vec![
313                "SELECT ANSI_BOLD('Important')",
314                "SELECT ANSI_BOLD(total) FROM sales",
315            ],
316        }
317    }
318
319    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
320        self.validate_args(args)?;
321
322        let text = match &args[0] {
323            DataValue::String(s) => s.clone(),
324            DataValue::InternedString(s) => s.to_string(),
325            DataValue::Integer(n) => n.to_string(),
326            DataValue::Float(f) => f.to_string(),
327            DataValue::Null => return Ok(DataValue::Null),
328            _ => args[0].to_string(),
329        };
330
331        Ok(DataValue::String(format!("\x1b[1m{}{}", text, ANSI_RESET)))
332    }
333}
334
335/// ANSI_ITALIC(text) - Make text italic
336pub struct AnsiItalicFunction;
337
338impl SqlFunction for AnsiItalicFunction {
339    fn signature(&self) -> FunctionSignature {
340        FunctionSignature {
341            name: "ANSI_ITALIC",
342            category: FunctionCategory::Terminal,
343            arg_count: ArgCount::Fixed(1),
344            description: "Make text italic using ANSI escape codes",
345            returns: "Italic text",
346            examples: vec!["SELECT ANSI_ITALIC('Emphasized')"],
347        }
348    }
349
350    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
351        self.validate_args(args)?;
352
353        let text = match &args[0] {
354            DataValue::String(s) => s.clone(),
355            DataValue::InternedString(s) => s.to_string(),
356            DataValue::Integer(n) => n.to_string(),
357            DataValue::Float(f) => f.to_string(),
358            DataValue::Null => return Ok(DataValue::Null),
359            _ => args[0].to_string(),
360        };
361
362        Ok(DataValue::String(format!("\x1b[3m{}{}", text, ANSI_RESET)))
363    }
364}
365
366/// ANSI_UNDERLINE(text) - Underline text
367pub struct AnsiUnderlineFunction;
368
369impl SqlFunction for AnsiUnderlineFunction {
370    fn signature(&self) -> FunctionSignature {
371        FunctionSignature {
372            name: "ANSI_UNDERLINE",
373            category: FunctionCategory::Terminal,
374            arg_count: ArgCount::Fixed(1),
375            description: "Underline text using ANSI escape codes",
376            returns: "Underlined text",
377            examples: vec!["SELECT ANSI_UNDERLINE('Important')"],
378        }
379    }
380
381    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
382        self.validate_args(args)?;
383
384        let text = match &args[0] {
385            DataValue::String(s) => s.clone(),
386            DataValue::InternedString(s) => s.to_string(),
387            DataValue::Integer(n) => n.to_string(),
388            DataValue::Float(f) => f.to_string(),
389            DataValue::Null => return Ok(DataValue::Null),
390            _ => args[0].to_string(),
391        };
392
393        Ok(DataValue::String(format!("\x1b[4m{}{}", text, ANSI_RESET)))
394    }
395}
396
397/// ANSI_BLINK(text) - Make text blink (slow)
398pub struct AnsiBlinkFunction;
399
400impl SqlFunction for AnsiBlinkFunction {
401    fn signature(&self) -> FunctionSignature {
402        FunctionSignature {
403            name: "ANSI_BLINK",
404            category: FunctionCategory::Terminal,
405            arg_count: ArgCount::Fixed(1),
406            description: "Make text blink using ANSI escape codes",
407            returns: "Blinking text",
408            examples: vec!["SELECT ANSI_BLINK('ALERT')"],
409        }
410    }
411
412    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
413        self.validate_args(args)?;
414
415        let text = match &args[0] {
416            DataValue::String(s) => s.clone(),
417            DataValue::InternedString(s) => s.to_string(),
418            DataValue::Integer(n) => n.to_string(),
419            DataValue::Float(f) => f.to_string(),
420            DataValue::Null => return Ok(DataValue::Null),
421            _ => args[0].to_string(),
422        };
423
424        Ok(DataValue::String(format!("\x1b[5m{}{}", text, ANSI_RESET)))
425    }
426}
427
428/// ANSI_REVERSE(text) - Reverse video (swap fg/bg)
429pub struct AnsiReverseFunction;
430
431impl SqlFunction for AnsiReverseFunction {
432    fn signature(&self) -> FunctionSignature {
433        FunctionSignature {
434            name: "ANSI_REVERSE",
435            category: FunctionCategory::Terminal,
436            arg_count: ArgCount::Fixed(1),
437            description: "Reverse video (swap foreground and background colors)",
438            returns: "Text with reversed colors",
439            examples: vec!["SELECT ANSI_REVERSE('Highlighted')"],
440        }
441    }
442
443    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
444        self.validate_args(args)?;
445
446        let text = match &args[0] {
447            DataValue::String(s) => s.clone(),
448            DataValue::InternedString(s) => s.to_string(),
449            DataValue::Integer(n) => n.to_string(),
450            DataValue::Float(f) => f.to_string(),
451            DataValue::Null => return Ok(DataValue::Null),
452            _ => args[0].to_string(),
453        };
454
455        Ok(DataValue::String(format!("\x1b[7m{}{}", text, ANSI_RESET)))
456    }
457}
458
459/// ANSI_STRIKETHROUGH(text) - Strikethrough text
460pub struct AnsiStrikethroughFunction;
461
462impl SqlFunction for AnsiStrikethroughFunction {
463    fn signature(&self) -> FunctionSignature {
464        FunctionSignature {
465            name: "ANSI_STRIKETHROUGH",
466            category: FunctionCategory::Terminal,
467            arg_count: ArgCount::Fixed(1),
468            description: "Add strikethrough to text using ANSI escape codes",
469            returns: "Strikethrough text",
470            examples: vec!["SELECT ANSI_STRIKETHROUGH('Deprecated')"],
471        }
472    }
473
474    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
475        self.validate_args(args)?;
476
477        let text = match &args[0] {
478            DataValue::String(s) => s.clone(),
479            DataValue::InternedString(s) => s.to_string(),
480            DataValue::Integer(n) => n.to_string(),
481            DataValue::Float(f) => f.to_string(),
482            DataValue::Null => return Ok(DataValue::Null),
483            _ => args[0].to_string(),
484        };
485
486        Ok(DataValue::String(format!("\x1b[9m{}{}", text, ANSI_RESET)))
487    }
488}