gobby_code/commands/status/
current.rs1use std::fmt::Write as _;
2use std::path::Path;
3
4use crate::config::Context;
5use crate::db;
6use crate::models::IndexedProject;
7use crate::output::{self, Format};
8use crate::utils::short_id;
9use crate::visibility;
10
11use super::shared::{format_coverage, format_timestamp, indexed_project_from_row};
12
13pub fn run(ctx: &Context, format: Format) -> anyhow::Result<()> {
14 let mut conn = db::connect_readonly(&ctx.database_url)?;
15
16 let stats: Option<IndexedProject> = conn
17 .query_opt(
18 "SELECT id,
19 root_path,
20 total_files::BIGINT AS total_files,
21 total_symbols::BIGINT AS total_symbols,
22 last_indexed_at::TEXT AS last_indexed_at,
23 COALESCE(index_duration_ms, 0)::BIGINT AS index_duration_ms,
24 NULL::BIGINT AS total_eligible_files
25 FROM code_indexed_projects WHERE id = $1",
26 &[&ctx.project_id],
27 )
28 .ok()
29 .flatten()
30 .and_then(|row| indexed_project_from_row(&row).ok());
31
32 match stats {
33 Some(s) => match format {
34 Format::Json => {
35 let mut value = serde_json::to_value(&s)?;
36 if let Some(overlay) = overlay_status_json(ctx, &mut conn) {
37 value["overlay"] = overlay;
38 }
39 output::print_json(&value)
40 }
41 Format::Text => {
42 let name = Path::new(&s.root_path)
43 .file_name()
44 .map(|n| n.to_string_lossy().to_string())
45 .unwrap_or_else(|| s.id.clone());
46 let mut text = String::new();
47 writeln!(text, "{} ({})", name, short_id(&s.id))?;
48 writeln!(text, " Root: {}", s.root_path)?;
49 writeln!(
50 text,
51 " Files: {}",
52 format_coverage(s.total_files, s.total_eligible_files)
53 )?;
54 writeln!(text, " Symbols: {}", s.total_symbols)?;
55 writeln!(text, " Indexed: {}", format_timestamp(&s.last_indexed_at))?;
56 write!(text, " Duration: {}ms", s.index_duration_ms)?;
57 if let crate::config::ProjectIndexScope::Overlay {
58 parent_project_id,
59 parent_root,
60 ..
61 } = &ctx.index_scope
62 {
63 writeln!(text)?;
64 write!(
65 text,
66 " Overlay: parent {} ({})",
67 parent_root.display(),
68 short_id(parent_project_id)
69 )?;
70 let tombstones = visibility::tombstone_count(&mut conn, ctx);
71 if tombstones > 0 {
72 writeln!(text)?;
73 write!(text, " Deletes: {tombstones}")?;
74 }
75 }
76 output::print_text(&text)
77 }
78 },
79 None => {
80 eprintln!(
81 "No index found for project {}. Run `gcode index` first.",
82 ctx.project_id
83 );
84 Ok(())
85 }
86 }
87}
88
89fn overlay_status_json(ctx: &Context, conn: &mut postgres::Client) -> Option<serde_json::Value> {
90 let crate::config::ProjectIndexScope::Overlay {
91 overlay_project_id,
92 overlay_root,
93 parent_project_id,
94 parent_root,
95 } = &ctx.index_scope
96 else {
97 return None;
98 };
99
100 let tombstones = visibility::tombstone_count(conn, ctx);
101 let mut overlay = serde_json::json!({
102 "overlay_project_id": overlay_project_id,
103 "overlay_root": overlay_root,
104 "parent_project_id": parent_project_id,
105 "parent_root": parent_root,
106 });
107 if tombstones > 0 {
108 overlay["tombstones"] = serde_json::json!(tombstones);
109 }
110 Some(overlay)
111}