1use crate::db::DB;
4use colored::Colorize;
5
6fn format_size(bytes: usize) -> String {
14 if bytes >= 1_048_576 {
15 format!("{:.2} MB", bytes as f64 / 1_048_576.0)
16 } else if bytes >= 1_024 {
17 format!("{:.2} KB", bytes as f64 / 1_024.0)
18 } else {
19 format!("{} B", bytes)
20 }
21}
22
23pub async fn stats(db: &DB) {
36 println!("{}", " Fetching database stats...".dimmed());
37
38 match crate::db::queries::stats::get_stats(db).await {
39 Err(e) => println!("{}", format!(" Failed to get stats: {}", e).red()),
40 Ok(stats) => {
41 println!();
42 println!("{}", " Database Stats".cyan().bold());
43 println!(
44 "{}",
45 " ─────────────────────────────────────────────────────".dimmed()
46 );
47 println!(
48 " {:<38} {:>8} {:>12}",
49 "Table".white().bold(),
50 "Rows".white().bold(),
51 "Est. Size".white().bold()
52 );
53 println!(
54 "{}",
55 " ─────────────────────────────────────────────────────".dimmed()
56 );
57
58 for t in &stats.tables {
59 let count_str = if t.count > 0 {
60 t.count.to_string().green().to_string()
61 } else {
62 t.count.to_string().dimmed().to_string()
63 };
64
65 println!(
66 " {:<38} {:>8} {:>12}",
67 t.name.white(),
68 count_str,
69 format_size(t.size_bytes).dimmed()
70 );
71 }
72
73 println!(
74 "{}",
75 " ─────────────────────────────────────────────────────".dimmed()
76 );
77 println!(
78 " {:<38} {:>8} {:>12}",
79 "Total".white().bold(),
80 stats.total_rows.to_string().cyan(),
81 format_size(stats.total_size_bytes).cyan()
82 );
83 println!();
84 }
85 }
86}
87
88pub async fn table(db: &DB, raw: &str) {
102 let parts: Vec<&str> = raw.splitn(2, ", ").collect();
103 let table_name = parts[0].trim();
104 let page: usize = parts
105 .get(1)
106 .and_then(|p| p.trim().parse().ok())
107 .unwrap_or(1);
108
109 if table_name.is_empty() {
110 println!("{}", "Usage: db:table <name>".red());
111 println!("{}", " db:table <name>, <page>".red());
112 return;
113 }
114
115 match crate::db::queries::stats::get_table(db, table_name, page).await {
116 Err(e) => println!("{}", format!(" {}", e).red()),
117 Ok(result) => {
118 println!();
119 println!(
120 " {} {} {}",
121 table_name.cyan().bold(),
122 format!("— {} records", result.total).dimmed(),
123 format!("(est. {})", format_size(result.size_bytes)).dimmed()
124 );
125 println!(
126 " {}",
127 format!("Page {} of {}", result.page, result.total_pages).dimmed()
128 );
129 println!(
130 "{}",
131 " ─────────────────────────────────────────────────────".dimmed()
132 );
133
134 if result.records.is_empty() {
135 println!("{}", " No records found.".dimmed());
136 } else {
137 for record in &result.records {
138 let pretty =
139 serde_json::to_string_pretty(record).unwrap_or_else(|_| record.to_string());
140 for line in pretty.lines() {
141 println!(" {}", line.white());
142 }
143 println!("{}", " ·".dimmed());
144 }
145 }
146
147 println!(
148 "{}",
149 " ─────────────────────────────────────────────────────".dimmed()
150 );
151 if result.total_pages > 1 {
152 println!(
153 " {}",
154 format!("Run `db:table {}, <page>` to navigate.", table_name).dimmed()
155 );
156 }
157 println!();
158 }
159 }
160}