obsidian-cli-inspector 1.0.3

Local-first CLI/TUI for indexing and querying Obsidian vaults
Documentation
use crate::config::Config;
use crate::db::Database;
use crate::logger::Logger;
use crate::query;
use anyhow::{Context, Result};

pub fn diagnose_orphans(
    config: &Config,
    exclude_templates: bool,
    exclude_daily: bool,
    logger: Option<&Logger>,
) -> Result<()> {
    let db_path = config.database_path();

    if !db_path.exists() {
        anyhow::bail!(
            "Database not found at: {}\nRun 'obsidian-cli init' first",
            db_path.display()
        );
    }

    let db = Database::open(&db_path)
        .with_context(|| format!("Failed to open database: {}", db_path.display()))?;

    // Check if database has been indexed
    let stats = db.get_stats().context("Failed to get database stats")?;
    if stats.note_count == 0 {
        anyhow::bail!(
            "Database is empty. Run 'obsidian-cli-inspector index' to index your vault first"
        );
    }

    let orphans = db
        .conn()
        .execute_query(|conn| query::get_orphans(conn, exclude_templates, exclude_daily))
        .context("Failed to get orphans")?;

    let dead_ends = db
        .conn()
        .execute_query(|conn| query::get_dead_ends(conn, exclude_templates, exclude_daily))
        .context("Failed to get dead ends")?;

    println!("=== ORPHANS (no incoming + no outgoing links) ===");
    if orphans.is_empty() {
        println!("No orphan notes found.");
    } else {
        println!("Found {} orphan note(s):\n", orphans.len());
        for (idx, note) in orphans.iter().enumerate() {
            println!(
                "{}. {} ({})\n   In: {}",
                idx + 1,
                note.note_title,
                note.note_path,
                note.note_path
            );
        }
    }

    println!("\n=== DEAD ENDS (has incoming but no outgoing links) ===");
    if dead_ends.is_empty() {
        println!("No dead-end notes found.");
    } else {
        println!("Found {} dead-end note(s):\n", dead_ends.len());
        for (idx, note) in dead_ends.iter().enumerate() {
            println!(
                "{}. {} ({})\n   In: {}",
                idx + 1,
                note.note_title,
                note.note_path,
                note.note_path
            );
        }
    }

    if let Some(log) = logger {
        let _ = log.print_and_log(
            "diagnose-orphans",
            &format!(
                "Found {} orphans, {} dead-ends",
                orphans.len(),
                dead_ends.len()
            ),
        );
    }

    Ok(())
}