1use ratatui::layout::Rect;
4use ratatui::style::{Color, Style};
5use ratatui::widgets::{Block, Borders, Paragraph, Wrap};
6use ratatui::Frame;
7
8use crate::models::{Column, Row, Value};
9
10#[derive(Default)]
12pub struct DetailPanel {
13 visible: bool,
14 scroll: u16,
15}
16
17impl DetailPanel {
18 pub fn toggle(&mut self) {
19 self.visible = !self.visible;
20 }
21
22 pub fn is_visible(&self) -> bool {
23 self.visible
24 }
25
26 pub fn scroll_up(&mut self) {
27 self.scroll = self.scroll.saturating_sub(1);
28 }
29
30 pub fn scroll_down(&mut self) {
31 self.scroll = self.scroll.saturating_add(1);
32 }
33
34 pub fn render(&self, frame: &mut Frame<'_>, area: Rect, columns: &[Column], row: &Row) {
35 let content = format_row_detail(columns, row);
36 let paragraph = Paragraph::new(content)
37 .block(Block::default().borders(Borders::ALL).title("Detail"))
38 .style(Style::default().fg(Color::White))
39 .scroll((self.scroll, 0))
40 .wrap(Wrap { trim: false });
41 frame.render_widget(paragraph, area);
42 }
43
44 pub fn render_empty(&self, frame: &mut Frame<'_>, area: Rect) {
45 let paragraph = Paragraph::new("No row selected")
46 .block(Block::default().borders(Borders::ALL).title("Detail"))
47 .style(Style::default().fg(Color::Gray));
48 frame.render_widget(paragraph, area);
49 }
50}
51
52fn format_row_detail(columns: &[Column], row: &Row) -> String {
53 let mut map = serde_json::Map::new();
54 for (idx, column) in columns.iter().enumerate() {
55 let value = row
56 .columns
57 .get(idx)
58 .map(value_to_json)
59 .unwrap_or(serde_json::Value::Null);
60 map.insert(column.name.clone(), value);
61 }
62 let json = serde_json::Value::Object(map);
63
64 let json_text = serde_json::to_string_pretty(&json).unwrap_or_else(|_| "{}".to_string());
65 let yaml_text = serde_yaml::to_string(&json).unwrap_or_else(|_| "".to_string());
66
67 format!("JSON\n{json_text}\n\nYAML\n{yaml_text}")
68}
69
70fn value_to_json(value: &Value) -> serde_json::Value {
71 match value {
72 Value::Null => serde_json::Value::Null,
73 Value::Bool(value) => serde_json::Value::Bool(*value),
74 Value::Int(value) => serde_json::Value::Number((*value).into()),
75 Value::Float(value) => serde_json::Number::from_f64(*value)
76 .map(serde_json::Value::Number)
77 .unwrap_or(serde_json::Value::Null),
78 Value::Text(value) => serde_json::Value::String(value.clone()),
79 Value::Bytes(bytes) => serde_json::Value::Array(
80 bytes
81 .iter()
82 .map(|byte| serde_json::Value::Number((*byte).into()))
83 .collect(),
84 ),
85 Value::Vector(values) => serde_json::Value::Array(
86 values
87 .iter()
88 .filter_map(|value| serde_json::Number::from_f64(*value as f64))
89 .map(serde_json::Value::Number)
90 .collect(),
91 ),
92 }
93}