cqlite_cli/repl/commands/
status.rs1use anyhow::Result;
6use colored::Colorize;
7use std::path::Path;
8use std::sync::Arc;
9use tokio::sync::RwLock;
10
11#[cfg(feature = "state_machine")]
12use cqlite_core::discovery::{CoverageBadge, DiscoveryService};
13
14pub async fn execute_status(
23 data_dir: Option<&Path>,
24 schema_registry: Option<Arc<RwLock<cqlite_core::schema::registry::SchemaRegistry>>>,
25) -> Result<()> {
26 #[cfg(not(feature = "state_machine"))]
27 {
28 let _ = data_dir; let _ = schema_registry; return Err(anyhow::anyhow!(
31 "Status command requires state_machine feature. Rebuild with --features state_machine"
32 ));
33 }
34
35 #[cfg(feature = "state_machine")]
36 {
37 let Some(data_dir) = data_dir else {
39 return Err(anyhow::anyhow!(
40 "Data directory not configured. Use :config data-dir <PATH>"
41 ));
42 };
43
44 let discovery_service = if let Some(registry) = schema_registry {
46 DiscoveryService::with_schema_registry(data_dir.to_path_buf(), None, registry)
47 } else {
48 DiscoveryService::new(data_dir.to_path_buf(), None)
49 };
50
51 println!("{}", "Scanning data directory...".dimmed());
53 let summary = discovery_service.scan().await?;
54
55 display_summary(&summary);
56
57 Ok(())
58 }
59}
60
61#[cfg(feature = "state_machine")]
62fn display_summary(summary: &cqlite_core::discovery::DiscoverySummary) {
63 use chrono::{DateTime, Local};
64
65 println!();
66 println!("{}", "=== Data Discovery Status ===".green().bold());
67 println!();
68
69 println!(
71 "{} {}",
72 "Data Directory:".cyan(),
73 summary.data_dir.display()
74 );
75 let datetime: DateTime<Local> = summary.timestamp.into();
76 println!(
77 "{} {}",
78 "Discovery Time:".cyan(),
79 datetime.format("%Y-%m-%d %H:%M:%S")
80 );
81 println!();
82
83 println!(
85 "{} {} keyspace(s), {} table(s)",
86 "Discovered:".cyan(),
87 summary.keyspaces.len(),
88 summary.tables.len()
89 );
90 println!(
91 "{} {} SSTable file(s)",
92 "SSTable Files:".cyan(),
93 summary.sstables_found
94 );
95 println!();
96
97 if !summary.keyspaces.is_empty() {
99 println!("{}", "Keyspaces:".green().bold());
100 for keyspace in &summary.keyspaces {
101 println!(" - {}", keyspace.yellow());
102 }
103 println!();
104 }
105
106 if !summary.tables.is_empty() {
108 println!("{} (showing first 10):", "Tables:".green().bold());
109 for table in summary.tables.iter().take(10) {
110 println!(" - {}", table.cyan());
111 }
112 if summary.tables.len() > 10 {
113 println!(" {} {} more...", "...".dimmed(), summary.tables.len() - 10);
114 }
115 println!();
116 }
117
118 if let Some(ref version) = summary.resolved_version {
120 println!("{} {}", "Cassandra Version:".cyan(), version);
121 } else {
122 println!("{} {}", "Cassandra Version:".cyan(), "unknown".dimmed());
123 }
124 println!();
125
126 if let Some(ref coverage) = summary.coverage {
128 println!("{}", "Schema Coverage:".green().bold());
129
130 let coverage_pct = coverage.coverage_percentage();
131 println!(
132 " {} {}%",
133 "Coverage:".cyan(),
134 format!("{:.1}", coverage_pct).bold()
135 );
136
137 println!(
138 " {} {}",
139 "Tables with schema:".cyan(),
140 coverage.tables_with_schema
141 );
142
143 if !coverage.tables_missing_schema.is_empty() {
144 println!(
145 " {} {} (showing first 5):",
146 "Tables missing schema:".yellow(),
147 coverage.tables_missing_schema.len()
148 );
149 for table in coverage.tables_missing_schema.iter().take(5) {
150 println!(" - {}", table.yellow());
151 }
152 if coverage.tables_missing_schema.len() > 5 {
153 println!(
154 " {} {} more...",
155 "...".dimmed(),
156 coverage.tables_missing_schema.len() - 5
157 );
158 }
159 println!(
160 " {}",
161 "Hint: Load schemas with :schema load <PATH>".dimmed()
162 );
163 }
164
165 if !coverage.schemas_without_data.is_empty() {
166 println!(
167 " {} {} (showing first 5):",
168 "Schemas without data:".yellow(),
169 coverage.schemas_without_data.len()
170 );
171 for schema in coverage.schemas_without_data.iter().take(5) {
172 println!(" - {}", schema.dimmed());
173 }
174 if coverage.schemas_without_data.len() > 5 {
175 println!(
176 " {} {} more...",
177 "...".dimmed(),
178 coverage.schemas_without_data.len() - 5
179 );
180 }
181 }
182 println!();
183 } else {
184 println!(
185 "{}",
186 "Schema coverage: Not available (no schemas loaded)".yellow()
187 );
188 println!("{}", "Hint: Load schemas with :schema load <PATH>".dimmed());
189 println!();
190 }
191
192 let (badge_symbol, badge_text) = match summary.badge {
194 CoverageBadge::Green => ("✅", "Green (≥95% coverage)".green()),
195 CoverageBadge::Yellow => ("⚠️ ", "Yellow (50-95% coverage)".yellow()),
196 CoverageBadge::Red => ("❌", "Red (<50% coverage or critical errors)".red()),
197 CoverageBadge::Unknown => ("❓", "Unknown (no schema loaded)".dimmed()),
198 };
199
200 println!(
201 "{} {} {}",
202 "Status:".cyan().bold(),
203 badge_symbol,
204 badge_text
205 );
206 println!();
207}