Skip to main content

quorum_core/cli/
db.rs

1//! This file includes direct server commands that interact or are about the surreal database/
2
3use crate::db::DB;
4use colored::Colorize;
5
6/// Format bytes into a human-readable string
7///
8/// # Arguments
9/// * `bytes` - The number of bytes to format.
10///
11/// # Returns
12/// * `String` - A human-readable string representing the size.
13fn 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
23/// Fetch and display database statistics
24///
25/// Fetches statistics about the database, including the number of rows and estimated size for each table, and prints them to the console.
26///
27/// # Arguments
28/// * `db` - A reference to the database connection.
29///
30/// # Example
31/// ```
32/// let db = DB::connect("database_url").await?;
33/// db::stats(&db).await;
34/// ```
35pub 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
88/// Fetch and display table data
89///
90/// Fetches data from a specific table in the database and prints it to the console.
91///
92/// # Arguments
93/// * `db` - A reference to the database connection.
94/// * `raw` - The raw command input string.
95///
96/// # Example
97/// ```
98/// let db = DB::connect("database_url").await?;
99/// db::table(&db, "users").await;
100/// ```
101pub 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}