heroforge-core 0.2.2

Pure Rust core library for reading and writing Fossil SCM repositories
Documentation
//! Advanced find operations example with ignore patterns.
//!
//! This example demonstrates the powerful find() builder:
//! - Glob pattern matching
//! - Ignore patterns (gitignore-style)
//! - Hidden file filtering
//! - Depth limiting
//! - Size filtering
//! - Directory scoping

use heroforge_core::{Repository, Result};
use std::fs;

fn main() -> Result<()> {
    // Create a temporary directory for the example
    let tmp_dir = std::env::temp_dir().join("heroforge_find_example");
    let _ = fs::remove_dir_all(&tmp_dir);
    fs::create_dir_all(&tmp_dir)?;
    let repo_path = tmp_dir.join("project.forge");

    println!("=== Advanced Find Operations Example ===\n");

    // Initialize repository with a realistic project structure
    let repo = Repository::init(&repo_path)?;

    // Create a realistic project structure
    let _init_hash = repo
        .commit_builder()
        .message("Create project structure")
        .author("developer")
        .initial()
        // Root files
        .file("README.md", b"# Project\n")
        .file("Cargo.toml", b"[package]\nname = \"myproject\"\n")
        .file("Cargo.lock", b"# lock file\n")
        .file(".gitignore", b"target/\n*.log\n")
        .file(".env", b"SECRET=abc123\n")
        .file(".env.example", b"SECRET=\n")
        // Source files
        .file("src/main.rs", b"fn main() {}\n")
        .file("src/lib.rs", b"pub mod api;\npub mod db;\n")
        .file("src/api/mod.rs", b"pub mod handlers;\npub mod routes;\n")
        .file("src/api/handlers.rs", b"pub fn handle() {}\n")
        .file("src/api/routes.rs", b"pub fn routes() {}\n")
        .file("src/db/mod.rs", b"pub mod models;\npub mod queries;\n")
        .file("src/db/models.rs", b"pub struct User {}\n")
        .file("src/db/queries.rs", b"pub fn find_user() {}\n")
        // Tests
        .file("tests/integration.rs", b"#[test] fn test() {}\n")
        .file("tests/api_test.rs", b"#[test] fn api() {}\n")
        // Docs
        .file("docs/README.md", b"# Documentation\n")
        .file("docs/api/endpoints.md", b"# API Endpoints\n")
        .file("docs/api/auth.md", b"# Authentication\n")
        .file("docs/guides/setup.md", b"# Setup Guide\n")
        // Config
        .file("config/default.toml", b"[server]\nport = 8080\n")
        .file("config/production.toml", b"[server]\nport = 80\n")
        // Build artifacts (simulating what would normally be ignored)
        .file(
            "target/debug/myproject",
            b"binary content here which is larger than other files to test size filtering",
        )
        .file("target/debug/deps/lib.rlib", b"rlib content\n")
        .file("node_modules/package/index.js", b"module.exports = {};\n")
        // Logs
        .file("logs/app.log", b"2024-01-01 INFO: Started\n")
        .file("logs/error.log", b"2024-01-01 ERROR: Something\n")
        // Hidden directories
        .file(".git/config", b"[core]\n")
        .file(".vscode/settings.json", b"{}\n")
        .execute()?;

    println!(
        "Created project with {} files\n",
        repo.files().on_trunk().list()?.len()
    );

    // =========================================================================
    // Basic Pattern Matching
    // =========================================================================
    println!("=== Basic Pattern Matching ===\n");

    // Find all Rust files
    let rust_files = repo.fs().find().pattern("**/*.rs").execute()?;
    println!("All Rust files ({} found):", rust_files.count);
    for f in &rust_files.files {
        println!("  {}", f.path);
    }
    println!();

    // Find all markdown files
    let md_files = repo.fs().find().pattern("**/*.md").paths()?;
    println!("All Markdown files ({} found):", md_files.len());
    for p in &md_files {
        println!("  {}", p);
    }
    println!();

    // Find all TOML files
    let toml_count = repo.fs().find().pattern("**/*.toml").count()?;
    println!("TOML files: {} found\n", toml_count);

    // =========================================================================
    // Ignore Patterns
    // =========================================================================
    println!("=== Ignore Patterns ===\n");

    // Find source files, ignoring tests
    let src_no_tests = repo
        .fs()
        .find()
        .pattern("**/*.rs")
        .ignore("tests/**")
        .paths()?;
    println!("Rust files (excluding tests):");
    for p in &src_no_tests {
        println!("  {}", p);
    }
    println!();

    // Use gitignore-style defaults
    let clean_files = repo
        .fs()
        .find()
        .pattern("**/*")
        .use_gitignore()
        .ignore("*.lock")
        .ignore("logs/**")
        .paths()?;
    println!(
        "Files with gitignore defaults ({} found):",
        clean_files.len()
    );
    for p in clean_files.iter().take(10) {
        println!("  {}", p);
    }
    if clean_files.len() > 10 {
        println!("  ... and {} more", clean_files.len() - 10);
    }
    println!();

    // =========================================================================
    // Hidden Files
    // =========================================================================
    println!("=== Hidden File Handling ===\n");

    // All files including hidden
    let all_files = repo.fs().find().pattern("**/*").count()?;
    println!("All files: {}", all_files);

    // Excluding hidden files
    let visible_files = repo.fs().find().pattern("**/*").ignore_hidden().count()?;
    println!("Visible files (no hidden): {}", visible_files);

    // Just hidden files
    let hidden_files = repo.fs().find().pattern("**/.*").paths()?;
    println!("Hidden files ({} found):", hidden_files.len());
    for p in &hidden_files {
        println!("  {}", p);
    }
    println!();

    // =========================================================================
    // Directory Scoping
    // =========================================================================
    println!("=== Directory Scoping ===\n");

    // Find only in src directory
    let src_files = repo.fs().find().in_dir("src").pattern("**/*.rs").paths()?;
    println!("Rust files in src/ ({} found):", src_files.len());
    for p in &src_files {
        println!("  {}", p);
    }
    println!();

    // Find only in docs directory
    let doc_files = repo.fs().find().in_dir("docs").pattern("**/*").paths()?;
    println!("Files in docs/ ({} found):", doc_files.len());
    for p in &doc_files {
        println!("  {}", p);
    }
    println!();

    // =========================================================================
    // Depth Limiting
    // =========================================================================
    println!("=== Depth Limiting ===\n");

    // Root level files only (depth 0)
    let root_files = repo.fs().find().max_depth(0).pattern("*").paths()?;
    println!("Root level files ({} found):", root_files.len());
    for p in &root_files {
        println!("  {}", p);
    }
    println!();

    // Up to depth 1
    let shallow_files = repo.fs().find().max_depth(1).pattern("**/*").paths()?;
    println!("Files up to depth 1 ({} found):", shallow_files.len());
    for p in &shallow_files {
        println!("  {}", p);
    }
    println!();

    // =========================================================================
    // Multiple Patterns
    // =========================================================================
    println!("=== Multiple Patterns ===\n");

    // Multiple include patterns
    let source_files = repo
        .fs()
        .find()
        .patterns(&["**/*.rs", "**/*.toml"])
        .ignore_hidden()
        .paths()?;
    println!("Rust and TOML files ({} found):", source_files.len());
    for p in &source_files {
        println!("  {}", p);
    }
    println!();

    // Multiple ignore patterns
    let clean_src = repo
        .fs()
        .find()
        .pattern("**/*")
        .ignore_patterns(&["target/**", "node_modules/**", "logs/**", "*.log", "*.lock"])
        .ignore_hidden()
        .paths()?;
    println!("Clean source files ({} found):", clean_src.len());
    for p in &clean_src {
        println!("  {}", p);
    }
    println!();

    // =========================================================================
    // Case Insensitive
    // =========================================================================
    println!("=== Case Insensitive Matching ===\n");

    // Find README files (any case)
    let readme_files = repo
        .fs()
        .find()
        .pattern("**/readme*")
        .ignore_case()
        .paths()?;
    println!("README files (case-insensitive):");
    for p in &readme_files {
        println!("  {}", p);
    }
    println!();

    // =========================================================================
    // Metadata
    // =========================================================================
    println!("=== Find with Metadata ===\n");

    let result = repo
        .fs()
        .find()
        .in_dir("src")
        .pattern("**/*.rs")
        .execute()?;
    println!("Search results for src/**/*.rs:");
    println!("  Files found: {}", result.count);
    println!("  Directories traversed: {}", result.dirs_traversed);
    println!("  Files:");
    for f in &result.files {
        println!(
            "    {} (hash: {}..., size: {:?})",
            f.path,
            &f.hash[..8],
            f.size
        );
    }
    println!();

    // =========================================================================
    // Practical Examples
    // =========================================================================
    println!("=== Practical Examples ===\n");

    // Find all API-related files
    let api_files = repo.fs().find().pattern("**/*api*").ignore_case().paths()?;
    println!("API-related files:");
    for p in &api_files {
        println!("  {}", p);
    }
    println!();

    // Find configuration files
    let config_files = repo
        .fs()
        .find()
        .patterns(&["**/*.toml", "**/*.json", "**/config/**"])
        .ignore_hidden()
        .paths()?;
    println!("Configuration files:");
    for p in &config_files {
        println!("  {}", p);
    }
    println!();

    // Find files that should be in .gitignore but aren't
    let should_ignore = repo
        .fs()
        .find()
        .patterns(&["target/**", "node_modules/**", "*.log", "*.lock"])
        .paths()?;
    println!("Files that should typically be gitignored:");
    for p in &should_ignore {
        println!("  {}", p);
    }

    // Cleanup
    let _ = fs::remove_dir_all(&tmp_dir);
    println!("\n=== Example completed successfully! ===");

    Ok(())
}