sql_cli/sql/generators/
ascii_art.rs

1use crate::data::datatable::{DataColumn, DataRow, DataTable, DataType, DataValue};
2use crate::sql::generators::TableGenerator;
3use anyhow::{anyhow, Result};
4use figlet_rs::FIGfont;
5use std::collections::HashMap;
6use std::sync::Arc;
7
8/// ASCII_ART - Generate ASCII art text using FIGlet fonts
9pub struct AsciiArt;
10
11impl TableGenerator for AsciiArt {
12    fn name(&self) -> &str {
13        "ASCII_ART"
14    }
15
16    fn columns(&self) -> Vec<DataColumn> {
17        vec![DataColumn {
18            name: "line".to_string(),
19            data_type: DataType::String,
20            nullable: false,
21            unique_values: Some(0),
22            null_count: 0,
23            metadata: HashMap::new(),
24            qualified_name: None,
25            source_table: None,
26        }]
27    }
28
29    fn generate(&self, args: Vec<DataValue>) -> Result<Arc<DataTable>> {
30        if args.is_empty() {
31            return Err(anyhow!("ASCII_ART requires at least 1 argument (text)"));
32        }
33
34        // Get text to convert
35        let text = match &args[0] {
36            DataValue::String(s) => s.clone(),
37            DataValue::Null => return Err(anyhow!("ASCII_ART text cannot be NULL")),
38            other => other.to_string(),
39        };
40
41        // Optional: font name (default to standard) - reserved for future use
42        let _font_name = if args.len() > 1 {
43            match &args[1] {
44                DataValue::String(s) => s.clone(),
45                DataValue::Null => "standard".to_string(),
46                _ => "standard".to_string(),
47            }
48        } else {
49            "standard".to_string()
50        };
51
52        // Load the font (for now, always use standard font)
53        // In the future, we could add more fonts based on _font_name
54        let font =
55            FIGfont::standard().map_err(|e| anyhow!("Failed to load standard font: {}", e))?;
56
57        // Convert text to ASCII art
58        let ascii_art = font
59            .convert(&text)
60            .ok_or_else(|| anyhow!("Failed to convert text to ASCII art"))?;
61
62        // Create table with one row per line
63        let mut table = DataTable::new("ascii_art");
64        table.add_column(DataColumn::new("line"));
65
66        // Split the ASCII art into lines and add as rows
67        for line in ascii_art.to_string().lines() {
68            table
69                .add_row(DataRow::new(vec![DataValue::String(line.to_string())]))
70                .map_err(|e| anyhow!(e))?;
71        }
72
73        Ok(Arc::new(table))
74    }
75
76    fn description(&self) -> &str {
77        "Generate ASCII art text using FIGlet fonts"
78    }
79
80    fn arg_count(&self) -> usize {
81        1 // Text is required, font is optional
82    }
83}
84
85/// BIG_TEXT - Alias for ASCII_ART
86pub struct BigText;
87
88impl TableGenerator for BigText {
89    fn name(&self) -> &str {
90        "BIG_TEXT"
91    }
92
93    fn columns(&self) -> Vec<DataColumn> {
94        AsciiArt.columns()
95    }
96
97    fn generate(&self, args: Vec<DataValue>) -> Result<Arc<DataTable>> {
98        AsciiArt.generate(args)
99    }
100
101    fn description(&self) -> &str {
102        "Generate big ASCII text (alias for ASCII_ART)"
103    }
104
105    fn arg_count(&self) -> usize {
106        1
107    }
108}
109
110/// BANNER - Create a banner with ASCII art and optional border
111pub struct Banner;
112
113impl TableGenerator for Banner {
114    fn name(&self) -> &str {
115        "BANNER"
116    }
117
118    fn columns(&self) -> Vec<DataColumn> {
119        vec![DataColumn {
120            name: "line".to_string(),
121            data_type: DataType::String,
122            nullable: false,
123            unique_values: Some(0),
124            null_count: 0,
125            metadata: HashMap::new(),
126            qualified_name: None,
127            source_table: None,
128        }]
129    }
130
131    fn generate(&self, args: Vec<DataValue>) -> Result<Arc<DataTable>> {
132        if args.is_empty() {
133            return Err(anyhow!("BANNER requires at least 1 argument (text)"));
134        }
135
136        // Get text to convert
137        let text = match &args[0] {
138            DataValue::String(s) => s.clone(),
139            DataValue::Null => return Err(anyhow!("BANNER text cannot be NULL")),
140            other => other.to_string(),
141        };
142
143        // Optional: border character (default to '*')
144        let border_char = if args.len() > 1 {
145            match &args[1] {
146                DataValue::String(s) if !s.is_empty() => s.chars().next().unwrap(),
147                _ => '*',
148            }
149        } else {
150            '*'
151        };
152
153        // Generate ASCII art
154        let font =
155            FIGfont::standard().map_err(|e| anyhow!("Failed to load standard font: {}", e))?;
156
157        let ascii_art = font
158            .convert(&text)
159            .ok_or_else(|| anyhow!("Failed to convert text to ASCII art"))?;
160
161        // Create table with banner
162        let mut table = DataTable::new("banner");
163        table.add_column(DataColumn::new("line"));
164
165        // Find the maximum line width
166        let lines: Vec<String> = ascii_art
167            .to_string()
168            .lines()
169            .map(|s| s.to_string())
170            .collect();
171        let max_width = lines.iter().map(|l| l.len()).max().unwrap_or(0);
172
173        // Create border line
174        let border_line = border_char.to_string().repeat(max_width + 4);
175
176        // Add top border
177        table
178            .add_row(DataRow::new(vec![DataValue::String(border_line.clone())]))
179            .map_err(|e| anyhow!(e))?;
180
181        // Add ASCII art lines with side borders
182        for line in lines {
183            let padded_line = format!(
184                "{} {:<width$} {}",
185                border_char,
186                line,
187                border_char,
188                width = max_width
189            );
190            table
191                .add_row(DataRow::new(vec![DataValue::String(padded_line)]))
192                .map_err(|e| anyhow!(e))?;
193        }
194
195        // Add bottom border
196        table
197            .add_row(DataRow::new(vec![DataValue::String(border_line)]))
198            .map_err(|e| anyhow!(e))?;
199
200        Ok(Arc::new(table))
201    }
202
203    fn description(&self) -> &str {
204        "Generate ASCII art banner with optional border"
205    }
206
207    fn arg_count(&self) -> usize {
208        1 // Text is required, border character is optional
209    }
210}