git_workty/commands/
doctor.rs

1use crate::config::{config_exists, Config};
2use crate::git::{is_git_installed, is_in_git_repo, GitRepo};
3use crate::worktree::list_worktrees;
4use owo_colors::OwoColorize;
5use std::path::Path;
6use std::process::Command;
7
8pub fn execute(start_path: Option<&Path>) {
9    let mut all_ok = true;
10
11    print_check("Git installed", is_git_installed(), &mut all_ok);
12
13    let cwd = start_path
14        .map(|p| p.to_path_buf())
15        .unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
16
17    let in_repo = is_in_git_repo(&cwd);
18    print_check("Inside Git repository", in_repo, &mut all_ok);
19
20    if !in_repo {
21        eprintln!("\n{}: Run this command from inside a Git repository.", "hint".cyan());
22        return;
23    }
24
25    let repo = match GitRepo::discover(start_path) {
26        Ok(r) => r,
27        Err(e) => {
28            print_fail(&format!("Failed to discover repo: {}", e));
29            return;
30        }
31    };
32
33    eprintln!("  Repository root: {}", repo.root.display());
34    eprintln!("  Common dir: {}", repo.common_dir.display());
35
36    let worktrees = list_worktrees(&repo);
37    print_check("Can list worktrees", worktrees.is_ok(), &mut all_ok);
38
39    if let Ok(wts) = &worktrees {
40        eprintln!("  Found {} worktree(s)", wts.len());
41
42        let prunable: Vec<_> = wts.iter().filter(|wt| wt.prunable).collect();
43        if !prunable.is_empty() {
44            print_warn(&format!("{} prunable worktree(s) found", prunable.len()));
45            eprintln!(
46                "  {}: Run `git worktree prune` to clean up.",
47                "hint".cyan()
48            );
49        }
50    }
51
52    print_check("Config exists", config_exists(&repo), &mut all_ok);
53
54    if config_exists(&repo) {
55        match Config::load(&repo) {
56            Ok(config) => {
57                eprintln!("  Base branch: {}", config.base);
58                eprintln!("  Workspace root: {}", config.root);
59            }
60            Err(e) => {
61                print_fail(&format!("Config parse error: {}", e));
62                all_ok = false;
63            }
64        }
65    } else {
66        eprintln!("  Using default config (no workty.toml found)");
67    }
68
69    let gh_installed = Command::new("gh")
70        .arg("--version")
71        .output()
72        .map(|o| o.status.success())
73        .unwrap_or(false);
74
75    if gh_installed {
76        eprintln!("  {} GitHub CLI (gh) available", "✓".green());
77
78        let gh_auth = Command::new("gh")
79            .args(["auth", "status"])
80            .output()
81            .map(|o| o.status.success())
82            .unwrap_or(false);
83
84        if gh_auth {
85            eprintln!("  {} GitHub CLI authenticated", "✓".green());
86        } else {
87            eprintln!("  {} GitHub CLI not authenticated", "○".yellow());
88            eprintln!("  {}: Run `gh auth login` to enable PR features.", "hint".cyan());
89        }
90    } else {
91        eprintln!("  {} GitHub CLI (gh) not installed (optional)", "○".dimmed());
92    }
93
94    eprintln!();
95    if all_ok {
96        eprintln!("{}", "All checks passed! ✓".green().bold());
97    } else {
98        eprintln!("{}", "Some checks failed. See hints above.".yellow());
99    }
100}
101
102fn print_check(name: &str, ok: bool, all_ok: &mut bool) {
103    if ok {
104        eprintln!("{} {}", "✓".green(), name);
105    } else {
106        eprintln!("{} {}", "✗".red(), name);
107        *all_ok = false;
108    }
109}
110
111fn print_fail(msg: &str) {
112    eprintln!("{} {}", "✗".red(), msg);
113}
114
115fn print_warn(msg: &str) {
116    eprintln!("{} {}", "!".yellow(), msg);
117}