Skip to main content

rusticity_term/ui/
query_editor.rs

1use ratatui::{
2    layout::Rect,
3    style::{Color, Style},
4    text::{Line, Span},
5    widgets::{Block, BorderType, Borders, Paragraph, Wrap},
6    Frame,
7};
8
9use super::styles;
10
11pub struct QueryEditorConfig<'a> {
12    pub query_text: &'a str,
13    pub cursor_line: usize,
14    pub cursor_col: usize,
15    pub is_active: bool,
16    pub title: &'a str,
17    pub area: Rect,
18}
19
20pub fn render_query_editor(frame: &mut Frame, config: QueryEditorConfig) {
21    let (border_style, border_type) = if config.is_active {
22        (styles::active_border(), BorderType::Double)
23    } else {
24        (Style::default(), BorderType::Plain)
25    };
26
27    let lines: Vec<Line> = config
28        .query_text
29        .lines()
30        .enumerate()
31        .map(|(line_idx, line_text)| {
32            if config.is_active && line_idx == config.cursor_line {
33                let before = &line_text[..config.cursor_col.min(line_text.len())];
34                let cursor_char = line_text
35                    .chars()
36                    .nth(config.cursor_col)
37                    .map(|c| c.to_string())
38                    .unwrap_or_else(|| " ".to_string());
39                let after =
40                    &line_text[(config.cursor_col + cursor_char.len()).min(line_text.len())..];
41
42                Line::from(vec![
43                    Span::raw(before),
44                    Span::styled(cursor_char, styles::bg_white().fg(Color::Black)),
45                    Span::raw(after),
46                ])
47            } else {
48                Line::from(line_text.to_string())
49            }
50        })
51        .collect();
52
53    let paragraph = Paragraph::new(lines)
54        .block(
55            Block::default()
56                .title(config.title)
57                .borders(Borders::ALL)
58                .border_type(BorderType::Rounded)
59                .border_style(border_style)
60                .border_type(border_type),
61        )
62        .wrap(Wrap { trim: false });
63
64    frame.render_widget(paragraph, config.area);
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn test_query_editor_config_creation() {
73        let config = QueryEditorConfig {
74            query_text: "SELECT * FROM logs",
75            cursor_line: 0,
76            cursor_col: 5,
77            is_active: true,
78            title: "Query",
79            area: Rect::new(0, 0, 80, 10),
80        };
81
82        assert_eq!(config.query_text, "SELECT * FROM logs");
83        assert_eq!(config.cursor_line, 0);
84        assert_eq!(config.cursor_col, 5);
85        assert!(config.is_active);
86    }
87
88    #[test]
89    fn test_query_editor_multiline() {
90        assert_eq!(
91            "fields @timestamp\n| sort @timestamp desc\n| limit 100"
92                .lines()
93                .count(),
94            3
95        );
96    }
97}