heroforge-core 0.2.2

Pure Rust core library for reading and writing Fossil SCM repositories
Documentation
//! Git import test example.
//!
//! This example imports a git repository into a heroforge repository,
//! verifies the file count matches using find on both sides.
//!
//! Run with:
//! ```bash
//! cargo run --example git_import_test --features git-import
//! ```

use heroforge_core::Repository;
use std::collections::HashSet;
use std::process::Command;

fn get_git_files(repo_path: &str) -> Result<HashSet<String>, Box<dyn std::error::Error>> {
    // Get all files in the git repo (excluding .git directory)
    let output = Command::new("find")
        .args([repo_path, "-type", "f", "-not", "-path", "*/.git/*"])
        .output()?;

    let stdout = String::from_utf8_lossy(&output.stdout);
    let base_path = format!("{}/", repo_path);

    let files: HashSet<String> = stdout
        .lines()
        .map(|line| line.replace(&base_path, ""))
        .collect();

    Ok(files)
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let git_url = "https://forge.ourworld.tf/geomind_research/herolib_rust.git";
    let fossil_path = "/tmp/herolib.forge";

    println!("=== Heroforge Git Import Test ===\n");

    // Remove existing file if present
    if std::path::Path::new(fossil_path).exists() {
        std::fs::remove_file(fossil_path)?;
        println!("Removed existing {}", fossil_path);
    }

    // Create a new heroforge repository
    println!("Creating new heroforge repository at {}", fossil_path);
    let repo = Repository::init(fossil_path)?;

    // Import from git
    println!("Importing from git: {}", git_url);
    println!("This may take a moment...\n");

    let commit_hash = repo
        .git_import()
        .url(git_url)
        .branch("main")
        .message("Import herolib_rust from git")
        .author("developer")
        .execute()?;

    println!("Import complete!");
    println!("Commit hash: {}\n", commit_hash);

    // Clone the git repo for comparison
    let temp_git_dir = "/tmp/herolib_rust_compare";
    if std::path::Path::new(temp_git_dir).exists() {
        std::fs::remove_dir_all(temp_git_dir)?;
    }

    println!("Cloning git repo for comparison...");
    let clone_output = Command::new("git")
        .args([
            "clone",
            "--depth",
            "1",
            "--branch",
            "main",
            git_url,
            temp_git_dir,
        ])
        .output()?;

    if !clone_output.status.success() {
        eprintln!(
            "Git clone failed: {}",
            String::from_utf8_lossy(&clone_output.stderr)
        );
        return Ok(());
    }

    // Get files from git using find
    let git_files = get_git_files(temp_git_dir)?;
    println!("Files in git repository (find): {}", git_files.len());

    // Get files from heroforge using find
    let forge_find_result = repo.fs().find().pattern("**/*").execute()?;
    let forge_files: HashSet<String> = forge_find_result
        .files
        .iter()
        .map(|f| f.path.clone())
        .collect();
    println!(
        "Files in heroforge repository (find): {}",
        forge_files.len()
    );

    // Compare file sets
    println!("\n=== Comparison ===");

    let in_git_not_forge: Vec<_> = git_files.difference(&forge_files).collect();
    let in_forge_not_git: Vec<_> = forge_files.difference(&git_files).collect();

    if in_git_not_forge.is_empty() && in_forge_not_git.is_empty() {
        println!("✓ All files match! ({} files)", git_files.len());
    } else {
        if !in_git_not_forge.is_empty() {
            println!(
                "\n✗ Files in git but NOT in heroforge ({}):",
                in_git_not_forge.len()
            );
            for f in in_git_not_forge.iter().take(10) {
                println!("  - {}", f);
            }
            if in_git_not_forge.len() > 10 {
                println!("  ... and {} more", in_git_not_forge.len() - 10);
            }
        }

        if !in_forge_not_git.is_empty() {
            println!(
                "\n✗ Files in heroforge but NOT in git ({}):",
                in_forge_not_git.len()
            );
            for f in in_forge_not_git.iter().take(10) {
                println!("  - {}", f);
            }
            if in_forge_not_git.len() > 10 {
                println!("  ... and {} more", in_forge_not_git.len() - 10);
            }
        }
    }

    // Clean up temp git directory
    std::fs::remove_dir_all(temp_git_dir)?;

    // Test various find patterns on heroforge
    println!("\n=== Pattern matching tests ===");

    // Find Rust files
    let rust_in_forge = repo.fs().find().pattern("**/*.rs").execute()?;
    let rust_count_forge = rust_in_forge.files.len();
    println!("Rust files (*.rs) in heroforge: {}", rust_count_forge);

    // Find TOML files
    let toml_in_forge = repo.fs().find().pattern("**/*.toml").execute()?;
    let toml_count_forge = toml_in_forge.files.len();
    println!("TOML files (*.toml) in heroforge: {}", toml_count_forge);

    // Find Markdown files
    let md_in_forge = repo.fs().find().pattern("**/*.md").execute()?;
    let md_count_forge = md_in_forge.files.len();
    println!("Markdown files (*.md) in heroforge: {}", md_count_forge);

    // Find in specific directory
    let packages_files = repo.fs().find().pattern("packages/**/*").execute()?;
    println!("Files in packages/: {}", packages_files.files.len());

    // Show sample of Rust files
    println!("\n=== Sample Rust files ===");
    for file in rust_in_forge.files.iter().take(15) {
        println!("  {}", file.path);
    }
    if rust_count_forge > 15 {
        println!("  ... and {} more", rust_count_forge - 15);
    }

    println!("\n=== Test Complete ===");
    println!("Repository saved at: {}", fossil_path);

    Ok(())
}