codesearch 0.1.14

A fast, intelligent CLI tool with multiple search modes (regex, fuzzy, semantic), code analysis, and dead code detection for popular programming languages
Documentation
//! Symbol Analysis Example
//!
//! Demonstrates how to use the symbol extraction and indexing system
//! to analyze code structure without LLM-based parsing.
//!
//! Run with: cargo run --example symbol_analysis -- <path>

use codesearch::symbols::{
    SymbolIndex, SymbolKind,
    extractor::extract_symbols_from_file,
    relationships::RelationshipGraph,
};
use std::env;
use std::path::Path;

fn main() {
    let args: Vec<String> = env::args().collect();
    let target_path = args.get(1).map(|s| s.as_str()).unwrap_or(".");

    println!("=== Symbol Analysis Example ===\n");
    println!("Analyzing: {}\n", target_path);

    let path = Path::new(target_path);

    if path.is_file() {
        analyze_single_file(path);
    } else if path.is_dir() {
        analyze_directory(path);
    } else {
        eprintln!("Path not found: {}", target_path);
        std::process::exit(1);
    }
}

fn analyze_single_file(file_path: &Path) {
    println!("Analyzing file: {}\n", file_path.display());

    match extract_symbols_from_file(file_path) {
        Ok(symbols) => {
            println!("Found {} symbols:\n", symbols.len());

            // Group symbols by kind
            let mut by_kind: std::collections::HashMap<String, Vec<_>> =
                std::collections::HashMap::new();
            for symbol in &symbols {
                by_kind
                    .entry(format!("{:?}", symbol.kind))
                    .or_default()
                    .push(symbol);
            }

            for (kind, items) in by_kind.iter().enumerate() {
                let (kind_name, items) = items;
                println!("  [{}] ({})", kind_name, items.len());
                for symbol in items {
                    let visibility = if symbol.is_public() { "pub" } else { "priv" };
                    let signature = if symbol.signature.is_empty() {
                        "".to_string()
                    } else {
                        format!(" {}", symbol.signature)
                    };
                    println!(
                        "    - {} {} at line {}{}",
                        visibility,
                        symbol.name,
                        symbol.line,
                        signature
                    );
                }
                println!();
            }

            // Show symbol details
            println!("\nDetailed Symbol Information:");
            for symbol in &symbols {
                println!("\n  {} (kind: {:?})", symbol.name, symbol.kind);
                println!("    File: {}", symbol.file_path);
                println!("    Line: {}", symbol.line);
                println!("    Visibility: {:?}", symbol.visibility);
                if let Some(ref parent) = symbol.parent {
                    println!("    Parent: {}", parent);
                }
                if let Some(ref doc) = symbol.documentation {
                    println!("    Documentation: {}", doc.trim());
                }
                if !symbol.signature.is_empty() {
                    println!("    Signature: {}", symbol.signature);
                }
            }
        }
        Err(e) => {
            eprintln!("Error extracting symbols: {}", e);
        }
    }
}

fn analyze_directory(dir_path: &Path) {
    println!("Analyzing directory: {}\n", dir_path.display());

    let index = SymbolIndex::new();
    let graph = RelationshipGraph::new();

    let mut total_files = 0;
    let mut total_symbols = 0;

    for entry in walkdir::WalkDir::new(dir_path)
        .into_iter()
        .filter_map(|e| e.ok())
    {
        let path = entry.path();
        if path.is_file() {
            if let Some(ext) = path.extension() {
                let ext = ext.to_string_lossy();
                if ext == "rs" || ext == "py" || ext == "js" || ext == "ts" || ext == "go" {
                    if let Ok(symbols) = extract_symbols_from_file(path) {
                        total_files += 1;
                        total_symbols += symbols.len();

                        for symbol in &symbols {
                            index.add_symbol(symbol.clone());
                            graph.add_symbol(symbol.clone());
                        }
                    }
                }
            }
        }
    }

    println!("Indexed {} symbols from {} files\n", total_symbols, total_files);

    // Show statistics
    let stats = index.get_stats();
    println!("Index Statistics:");
    println!("  Total symbols: {}", stats.total_symbols);
    println!("  Total files: {}", stats.total_files);

    if !stats.symbols_by_kind.is_empty() {
        println!("\n  Symbols by kind:");
        let mut kinds: Vec<_> = stats.symbols_by_kind.iter().collect();
        kinds.sort_by(|a, b| b.1.cmp(a.1));
        for (kind, count) in kinds {
            println!("    {}: {}", kind, count);
        }
    }

    if !stats.symbols_by_language.is_empty() {
        println!("\n  Symbols by language:");
        let mut langs: Vec<_> = stats.symbols_by_language.iter().collect();
        langs.sort_by(|a, b| b.1.cmp(a.1));
        for (lang, count) in langs {
            println!("    {}: {}", lang, count);
        }
    }

    // Find all functions
    let functions = index.get_symbols_by_kind(&SymbolKind::Function);
    if !functions.is_empty() {
        println!("\n  Functions found ({}):", functions.len());
        for func in functions.iter().take(10) {
            println!("    - {} ({}:{})", func.name, func.file_path, func.line);
        }
        if functions.len() > 10 {
            println!("    ... and {} more", functions.len() - 10);
        }
    }

    // Find all classes/structs
    let classes = index.get_symbols_by_kind(&SymbolKind::Class);
    let structs = index.get_symbols_by_kind(&SymbolKind::Struct);
    let types: Vec<_> = classes.into_iter().chain(structs.into_iter()).collect();
    if !types.is_empty() {
        println!("\n  Types found ({}):", types.len());
        for t in types.iter().take(10) {
            println!("    - {} ({}:{})", t.name, t.file_path, t.line);
        }
    }

    // Demonstrate search
    println!("\n\nSearch Demo:");
    println!("  Searching for 'main'...");
    let results = index.find_by_name("main");
    if results.is_empty() {
        println!("    No exact matches found.");
    } else {
        for r in &results {
            println!("    Found: {} at {}:{}", r.name, r.file_path, r.line);
        }
    }
}