Skip to main content

sql_rs/storage/
stats.rs

1/// Database statistics module
2use 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        // Truncate from the beginning to show filename
62        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}