1use serde::{Deserialize, Serialize};
3use std::path::Path;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct DatabaseStats {
7 pub database_path: String,
8 pub total_pages: usize,
9 pub page_size: usize,
10 pub total_size_bytes: u64,
11 pub total_size_mb: f64,
12 pub vector_collections: usize,
13 pub tables: Vec<TableStats>,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct TableStats {
18 pub name: String,
19 pub row_count: usize,
20 pub indexes: usize,
21}
22
23impl DatabaseStats {
24 pub fn format_human_readable(&self) -> String {
25 let mut output = String::new();
26 output.push_str("╔════════════════════════════════════════════════════════╗\n");
27 output.push_str("║ SQL-RS Database Statistics ║\n");
28 output.push_str("╠════════════════════════════════════════════════════════╣\n");
29 output.push_str(&format!("║ Database Path: {:<44} ║\n",
30 self.truncate_path(&self.database_path, 44)));
31 output.push_str("╠────────────────────────────────────────────────────────╣\n");
32 output.push_str(&format!("║ Total Pages: {:<44} ║\n", self.total_pages));
33 output.push_str(&format!("║ Page Size: {:<44} ║\n",
34 format!("{} bytes", self.page_size)));
35 output.push_str(&format!("║ Total Size: {:<44} ║\n",
36 format!("{:.2} MB", self.total_size_mb)));
37 output.push_str("╠────────────────────────────────────────────────────────╣\n");
38 output.push_str(&format!("║ Vector Collections: {:<41} ║\n", self.vector_collections));
39 output.push_str(&format!("║ Tables: {:<49} ║\n", self.tables.len()));
40 output.push_str("╠────────────────────────────────────────────────────────╣\n");
41
42 if self.tables.is_empty() {
43 output.push_str("║ No tables found ║\n");
44 } else {
45 output.push_str("║ Tables: ║\n");
46 for table in &self.tables {
47 output.push_str(&format!("║ • {:<20} Rows: {:<10} Indexes: {:<5} ║\n",
48 table.name, table.row_count, table.indexes));
49 }
50 }
51
52 output.push_str("╚════════════════════════════════════════════════════════╝\n");
53 output
54 }
55
56 fn truncate_path(&self, path: &str, max_len: usize) -> String {
57 if path.len() <= max_len {
58 return path.to_string();
59 }
60
61 let filename = Path::new(path)
63 .file_name()
64 .and_then(|n| n.to_str())
65 .unwrap_or(path);
66
67 if filename.len() <= max_len - 3 {
68 format!("...{}", filename)
69 } else {
70 format!("...{}", &filename[filename.len() - (max_len - 3)..])
71 }
72 }
73}
74
75use super::file::FileManager;
76use parking_lot::RwLock;
77use std::sync::Arc;
78
79impl DatabaseStats {
80 pub fn collect(file_manager: Arc<RwLock<FileManager>>, vector_collections: usize, tables: Vec<TableStats>) -> Result<Self, crate::SqlRsError> {
81 let fm = file_manager.read();
82 let total_pages = fm.page_count() as usize;
83 let page_size = fm.get_page_size();
84 let total_size_bytes = total_pages as u64 * page_size as u64;
85 let total_size_mb = total_size_bytes as f64 / (1024.0 * 1024.0);
86
87 Ok(Self {
88 database_path: fm.get_path().to_string_lossy().to_string(),
89 total_pages,
90 page_size,
91 total_size_bytes,
92 total_size_mb,
93 vector_collections,
94 tables,
95 })
96 }
97}