heroforge

Crates.ioheroforge
lib.rsheroforge
version0.2.1
created_at2025-12-16 21:06:45.981333+00
updated_at2025-12-17 06:39:29.977491+00
descriptionPure Rust client library for reading and writing Fossil SCM repositories
homepagehttps://github.com/herocode/heroforge
repositoryhttps://github.com/herocode/heroforge
max_upload_size
id1988735
size475,981
kristof de spiegeleer (despiegk)

documentation

https://docs.rs/heroforge

README

heroforge

Crates.io Documentation License

Overview

heroforge provides a complete API for interacting with Heroforge repositories programmatically, without requiring the Heroforge CLI to be installed. It supports both reading from existing repositories and creating new ones from scratch.

Features

Read Operations

  • Open and read existing Heroforge repositories
  • Browse repository history and check-ins
  • List and read files at any check-in
  • Find files using glob patterns
  • Navigate directory structures
  • Access branch and tag information

Write Operations

  • Create new repositories from scratch
  • Commit files with full manifest generation
  • Create and manage branches
  • Add tags to check-ins
  • Manage users and permissions

Filesystem Operations

  • Copy, move, rename files and directories
  • Delete files and directories
  • Change permissions (chmod)
  • Create symbolic links
  • Advanced find with ignore patterns

Synchronization

  • QUIC sync - Modern UDP-based protocol with TLS 1.3 (requires sync-quic feature)

Installation

Add heroforge to your Cargo.toml:

[dependencies]
heroforge = "0.1"

Optional Features

Enable QUIC sync for remote repository synchronization:

[dependencies]
heroforge = { version = "0.1", features = ["sync-quic"] }

Quick Start

Reading from an Existing Repository

use heroforge::Repository;

fn main() -> heroforge::Result<()> {
    // Open a Heroforge repository
    let repo = Repository::open("project.heroforge")?;

    // Get the latest check-in on trunk
    let tip = repo.history().trunk_tip()?;
    println!("Latest: {} by {}", tip.hash, tip.user);
    println!("Comment: {}", tip.comment);

    // List all files on trunk
    let files = repo.files().on_trunk().list()?;
    for file in &files {
        println!("  {}", file.name);
    }

    // Read a specific file from trunk
    let content = repo.files().on_trunk().read("README.md")?;
    println!("README:\n{}", String::from_utf8_lossy(&content));

    Ok(())
}

Creating a New Repository

use heroforge::Repository;

fn main() -> heroforge::Result<()> {
    // Create a new repository
    let repo = Repository::init("new_project.heroforge")?;

    // Create initial check-in
    let init_hash = repo.commit_builder()
        .message("initial empty check-in")
        .author("admin")
        .initial()
        .execute()?;

    // Add some files in a new commit
    let commit_hash = repo.commit_builder()
        .message("Initial project structure")
        .author("developer")
        .parent(&init_hash)
        .file("README.md", b"# My Project\n")
        .file("src/main.rs", b"fn main() { println!(\"Hello!\"); }\n")
        .execute()?;

    // Tag the release
    repo.tags()
        .create("v1.0.0")
        .at_commit(&commit_hash)
        .author("developer")
        .execute()?;

    // Create a feature branch
    repo.branches()
        .create("feature-x")
        .from_commit(&commit_hash)
        .author("developer")
        .execute()?;

    Ok(())
}

Finding Files with Glob Patterns

use heroforge::Repository;

fn main() -> heroforge::Result<()> {
    let repo = Repository::open("project.heroforge")?;

    // Find all Rust files on trunk
    let rust_files = repo.files().on_trunk().find("**/*.rs")?;
    for file in rust_files {
        println!("Found: {}", file.name);
    }

    // Find files in a specific directory on a branch
    let src_files = repo.files().on_branch("feature-x").find("src/**/*")?;
    
    // Find files at a specific tag
    let tagged_files = repo.files().at_tag("v1.0.0").find("*.md")?;
    
    Ok(())
}

Browsing History

use heroforge::Repository;

fn main() -> heroforge::Result<()> {
    let repo = Repository::open("project.heroforge")?;

    // Get recent check-ins
    let history = repo.history().recent(10)?;
    for checkin in history {
        println!("{} | {} | {}", 
            &checkin.hash[..12], 
            checkin.user, 
            checkin.comment
        );
    }

    // List all branches
    let branches = repo.branches().list()?;
    for branch in branches {
        println!("Branch: {}", branch);
    }

    // List all tags
    let tags = repo.tags().list()?;
    for tag in tags {
        println!("Tag: {}", tag);
    }

    // Get the tip of a specific branch
    let feature_tip = repo.history().branch_tip("feature-x")?;
    println!("Feature branch tip: {}", feature_tip.hash);

    Ok(())
}

User Management

use heroforge::Repository;

fn main() -> heroforge::Result<()> {
    let repo = Repository::init("project.heroforge")?;

    // Create a new user
    repo.users()
        .create("developer")
        .password("secret123")
        .capabilities("ei")  // edit + check-in
        .execute()?;

    // List all users
    let users = repo.users().list()?;
    for (login, caps) in users {
        println!("User: {} ({})", login, caps);
    }

    // Get user capabilities
    if let Some(caps) = repo.users().get_capabilities("developer")? {
        println!("Developer capabilities: {}", caps);
    }

    Ok(())
}

QUIC Synchronization

use heroforge::Repository;

fn main() -> heroforge::Result<()> {
    let repo = Repository::open("project.heroforge")?;
    
    // Sync with a remote repository over QUIC
    repo.sync()
        .url("quic://heroforge.example.com:4443/repo")
        .execute()?;
    
    Ok(())
}

Filesystem Operations

use heroforge::Repository;

fn main() -> heroforge::Result<()> {
    let repo = Repository::open_rw("project.heroforge")?;

    // Copy, move, delete files atomically
    let hash = repo.fs().modify()
        .message("Reorganize project")
        .author("developer")
        .copy_file("README.md", "docs/README.md")
        .move_dir("scripts", "tools")
        .delete_file("old_config.txt")
        .make_executable("tools/build.sh")
        .symlink("build", "tools/build.sh")
        .execute()?;

    // Advanced find with ignore patterns
    let files = repo.fs().find()
        .pattern("**/*.rs")
        .ignore("target/**")
        .ignore_hidden()
        .max_depth(3)
        .paths()?;
    
    // Utility functions
    println!("Exists: {}", repo.fs().exists("README.md")?);
    println!("Is dir: {}", repo.fs().is_dir("src")?);
    println!("Total size: {} bytes", repo.fs().du("**/*.rs")?);

    Ok(())
}

Builder API Overview

The library uses a fluent builder pattern for all operations:

Builder Entry Point Purpose
FilesBuilder repo.files() Read files, list directories, find with glob patterns
CommitBuilder repo.commit_builder() Create new check-ins with files
BranchesBuilder repo.branches() List and create branches
TagsBuilder repo.tags() List, create, and resolve tags
HistoryBuilder repo.history() Browse commits and history
UsersBuilder repo.users() Manage repository users
SyncBuilder repo.sync() Synchronize with remote repositories (QUIC)
FsOpsBuilder repo.fs() Filesystem operations (copy, move, delete, chmod, find, symlinks)

Using heroforge in Your Project

As a Library Dependency

  1. Add to your Cargo.toml:
[dependencies]
heroforge = "0.1"
  1. Import and use:
use heroforge::{Repository, Result, FossilError};

fn main() -> Result<()> {
    let repo = Repository::open("my-repo.heroforge")?;
    // ... your code
    Ok(())
}

Building a CLI Tool

use heroforge::Repository;
use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();
    
    if args.len() < 2 {
        eprintln!("Usage: {} <repository.heroforge>", args[0]);
        std::process::exit(1);
    }
    
    match Repository::open(&args[1]) {
        Ok(repo) => {
            match repo.history().trunk_tip() {
                Ok(tip) => {
                    println!("Repository: {}", args[1]);
                    println!("Latest commit: {}", tip.hash);
                    println!("Author: {}", tip.user);
                    println!("Message: {}", tip.comment);
                }
                Err(e) => eprintln!("Error reading tip: {}", e),
            }
        }
        Err(e) => eprintln!("Error opening repository: {}", e),
    }
}

Repository Compatibility

Repositories created with heroforge are fully compatible with the Heroforge CLI:

  • Use heroforge ui to browse repositories created by this library
  • heroforge can read repositories created by the Heroforge CLI
  • Sync with remote Heroforge servers works seamlessly

Feature Flags

Feature Description Dependencies
sync-quic QUIC protocol sync quinn, rustls, tokio

Examples

The repository includes many examples demonstrating various features:

# Basic repository reading
cargo run --example read_repo

# Find files with glob patterns
cargo run --example find_and_read

# Branches and tags demonstration
cargo run --example branch_tag_test

# Comprehensive API demo
cargo run --example comprehensive_test

# Filesystem operations (copy, move, delete, chmod, symlinks)
cargo run --example fs_operations

# Advanced find with ignore patterns
cargo run --example fs_find

# QUIC sync (requires sync-quic feature)
cargo run --example quic_incremental_sync_test --features sync-quic

Documentation

Full API documentation is available at docs.rs/heroforge.

Generate local documentation with:

cargo doc --open --all-features

Architecture

A Heroforge repository is a SQLite database containing:

  • Blobs: Compressed file contents and manifests (zlib)
  • Manifests: Check-in metadata with file lists, timestamps, comments
  • Tags: Branch names, version tags, and other labels
  • Events: Timeline of repository activity
  • Delta encoding: Efficient storage of similar content

Many thanks to

  • The Fossil SCM project for creating and maintaining Fossil on which this is based.
Commit count: 0

cargo fmt