Skip to main content

systemprompt_database/services/
display.rs

1use std::io::Write;
2
3use crate::models::{ColumnInfo, DatabaseInfo, QueryResult, TableInfo};
4
5pub trait DatabaseCliDisplay {
6    fn display_with_cli(&self);
7}
8
9fn stdout_writeln(args: std::fmt::Arguments<'_>) {
10    let mut stdout = std::io::stdout();
11    let _ = writeln!(stdout, "{args}");
12}
13
14impl DatabaseCliDisplay for Vec<TableInfo> {
15    fn display_with_cli(&self) {
16        if self.is_empty() {
17            stdout_writeln(format_args!("No tables found"));
18        } else {
19            stdout_writeln(format_args!("Tables:"));
20            for table in self {
21                stdout_writeln(format_args!("  {} (rows: {})", table.name, table.row_count));
22            }
23        }
24    }
25}
26
27impl DatabaseCliDisplay for (Vec<ColumnInfo>, i64) {
28    fn display_with_cli(&self) {
29        let (columns, _) = self;
30        stdout_writeln(format_args!("Columns:"));
31        for col in columns {
32            let default_display = col
33                .default
34                .as_deref()
35                .map_or_else(String::new, |d| format!("DEFAULT {d}"));
36
37            stdout_writeln(format_args!(
38                "  {} {} {} {} {}",
39                col.name,
40                col.data_type,
41                if col.nullable { "NULL" } else { "NOT NULL" },
42                if col.primary_key { "PK" } else { "" },
43                default_display
44            ));
45        }
46    }
47}
48
49impl DatabaseCliDisplay for DatabaseInfo {
50    fn display_with_cli(&self) {
51        stdout_writeln(format_args!("Database Info:"));
52        stdout_writeln(format_args!("  Path: {}", self.path));
53        stdout_writeln(format_args!("  Version: {}", self.version));
54        stdout_writeln(format_args!("  Tables: {}", self.tables.len()));
55    }
56}
57
58impl DatabaseCliDisplay for QueryResult {
59    fn display_with_cli(&self) {
60        if self.columns.is_empty() {
61            stdout_writeln(format_args!("No data returned"));
62            return;
63        }
64
65        stdout_writeln(format_args!("{}", self.columns.join(" | ")));
66        stdout_writeln(format_args!("{}", "-".repeat(80)));
67
68        for row in &self.rows {
69            let values: Vec<String> = self
70                .columns
71                .iter()
72                .map(|col| {
73                    row.get(col).map_or_else(
74                        || "NULL".to_string(),
75                        |v| match v {
76                            serde_json::Value::String(s) => s.clone(),
77                            serde_json::Value::Null => "NULL".to_string(),
78                            serde_json::Value::Bool(_)
79                            | serde_json::Value::Number(_)
80                            | serde_json::Value::Array(_)
81                            | serde_json::Value::Object(_) => v.to_string(),
82                        },
83                    )
84                })
85                .collect();
86            stdout_writeln(format_args!("{}", values.join(" | ")));
87        }
88
89        stdout_writeln(format_args!(
90            "\n{} rows returned in {}ms",
91            self.row_count, self.execution_time_ms
92        ));
93    }
94}