# Heroforge Usage Guide
A complete guide for using the `heroforge` Rust crate - a pure Rust client library for reading and writing Fossil SCM repositories.
**License:** Apache-2.0
## Requirements
- **Rust version:** 1.92.0 or later (`rustup update stable`)
- **Edition:** 2024
## Quick Start
### Installation
Add heroforge to your project:
```bash
cargo add heroforge
```
Or add to your `Cargo.toml`:
```toml
[dependencies]
heroforge = "0.2.1"
```
### Optional Features
Enable QUIC sync for remote repository synchronization:
```toml
[dependencies]
heroforge = { version = "0.2.2", features = ["sync-quic"] }
```
### Minimal Example
```rust
use heroforge::Repository;
fn main() -> heroforge::Result<()> {
// Open an existing repository
let repo = Repository::open("project.fossil")?;
// List files on trunk
let files = repo.files().on_trunk().list()?;
for file in files {
println!("{}", file.name);
}
Ok(())
}
```
## API Overview
The library uses a fluent builder pattern. All operations start from `Repository`:
| `FilesBuilder` | `repo.files()` | Read files, list directories, find with glob |
| `CommitBuilder` | `repo.commit()` | Create new commits 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 |
| `FsOpsBuilder` | `repo.fs()` | Filesystem ops (copy, move, delete, chmod, symlinks, find) |
| `SyncBuilder` | `repo.sync()` | Synchronize with remote (QUIC) |
## Repository Operations
### Opening Repositories
```rust
use heroforge::Repository;
// Open read-only
let repo = Repository::open("project.fossil")?;
// Open for read-write operations
let repo = Repository::open_rw("project.fossil")?;
// Create a new repository
let repo = Repository::init("new_project.fossil")?;
```
## File Operations
Access via `repo.files()`:
### List Files
```rust
// List all files on trunk
let files = repo.files().on_trunk().list()?;
// List files on a specific branch
let files = repo.files().on_branch("feature-x").list()?;
// List files at a specific tag
let files = repo.files().at_tag("v1.0.0").list()?;
// List files at a specific commit
let files = repo.files().at_commit("abc123...").list()?;
// List files in a directory
let files = repo.files().on_trunk().in_dir("src").list()?;
// List subdirectories
let subdirs = repo.files().on_trunk().subdirs("src")?;
```
### Find Files (Glob Patterns)
```rust
// Find all Rust files
let rust_files = repo.files().on_trunk().find("**/*.rs")?;
// Find files in src directory
let src_files = repo.files().on_branch("main").find("src/**/*.rs")?;
// Find markdown files at a tag
let docs = repo.files().at_tag("v1.0").find("**/*.md")?;
```
### Read Files
```rust
// Read file as bytes
let content: Vec<u8> = repo.files().on_trunk().read("README.md")?;
// Read file as string
let text: String = repo.files().on_trunk().read_string("README.md")?;
// Read from a branch
let config = repo.files().on_branch("develop").read_string("config.json")?;
```
## Commit Operations
Access via `repo.commit()`:
```rust
// Create initial commit (new repository)
let init_hash = repo.commit()
.message("Initial commit")
.author("admin")
.initial()
.execute()?;
// Add files in a new commit
let hash = repo.commit()
.message("Add project structure")
.author("developer")
.parent(&init_hash)
.file("README.md", b"# My Project\n")
.file("src/main.rs", b"fn main() { println!(\"Hello\"); }\n")
.file("Cargo.toml", b"[package]\nname = \"myproject\"\n")
.execute()?;
// Commit on a specific branch
let feature_hash = repo.commit()
.message("Add authentication")
.author("developer")
.parent(&some_hash)
.branch("feature-auth")
.file("src/auth.rs", b"pub fn login() {}\n")
.execute()?;
// Add multiple files at once
let files = vec![
("file1.txt", b"content1".as_slice()),
("file2.txt", b"content2".as_slice()),
];
let hash = repo.commit()
.message("Bulk add")
.author("developer")
.parent(&parent)
.files(&files)
.execute()?;
// Add file from disk
let hash = repo.commit()
.message("Add config")
.author("developer")
.parent(&parent)
.file_from_path("config.json", "/path/to/local/config.json")?
.execute()?;
```
## Branch Operations
Access via `repo.branches()`:
```rust
// List all branches
let branches: Vec<String> = repo.branches().list()?;
// Get a branch reference
let main = repo.branches().get("trunk")?;
let tip = main.tip()?; // Get latest commit
println!("Branch: {}, Tip: {}", main.name(), tip.hash);
// Create a new branch from another branch
repo.branches()
.create("feature-x")
.from_branch("trunk")
.author("developer")
.execute()?;
// Create branch from a tag
repo.branches()
.create("hotfix-1.0")
.from_tag("v1.0.0")
.author("developer")
.execute()?;
// Create branch from a specific commit
repo.branches()
.create("experiment")
.from_commit("abc123...")
.author("developer")
.execute()?;
```
## Tag Operations
Access via `repo.tags()`:
```rust
// List all tags
let tags: Vec<String> = repo.tags().list()?;
// Get a tag reference
let tag = repo.tags().get("v1.0.0")?;
let hash = tag.commit_hash()?;
println!("Tag: {} -> {}", tag.name(), hash);
// Create a tag on trunk tip
repo.tags()
.create("v1.0.0")
.at_branch("trunk")
.author("developer")
.execute()?;
// Create a tag at a specific commit
repo.tags()
.create("v1.0.1")
.at_commit("abc123...")
.author("developer")
.execute()?;
```
## History Operations
Access via `repo.history()`:
```rust
// Get trunk tip (latest commit)
let tip = repo.history().trunk_tip()?;
println!("Latest: {} by {} - {}", tip.hash, tip.user, tip.comment);
// Get branch tip
let feature_tip = repo.history().branch_tip("feature-x")?;
// Get recent commits
let recent = repo.history().recent(10)?;
for commit in recent {
println!("{} | {} | {}", &commit.hash[..12], commit.user, commit.comment);
}
// Get a specific commit
let commit = repo.history().get("abc123...")?;
// Query commits on a branch with limit
let commits = repo.history()
.on_branch("trunk")
.limit(20)
.list()?;
```
## Filesystem Operations
Access via `repo.fs()`. These provide high-level file manipulation with atomic commits.
### Modify Operations (Copy, Move, Delete)
```rust
// All modifications require message and author, and create a commit
let hash = repo.fs().modify()
.message("Reorganize project structure")
.author("developer")
.copy_file("README.md", "docs/README.md")
.copy_dir("src", "backup/src")
.move_file("old.txt", "archive/old.txt")
.move_dir("scripts", "tools")
.delete_file("temp.log")
.delete_dir("cache")
.execute()?;
// Delete files matching a pattern
repo.fs().modify()
.message("Clean up backup files")
.author("developer")
.delete_matching("**/*.bak")
.delete_matching("**/*.tmp")
.execute()?;
```
### Permission Operations
```rust
repo.fs().modify()
.message("Fix permissions")
.author("developer")
.make_executable("scripts/build.sh")
.chmod("config.toml", 0o644) // rw-r--r--
.chmod("secret.key", 0o600) // rw-------
.chmod_dir("bin", 0o755) // Recursive
.execute()?;
```
### Symlink Operations
```rust
repo.fs().modify()
.message("Add convenience symlinks")
.author("developer")
.symlink("current", "releases/v2.0.0")
.symlink("build", "scripts/build.sh")
.execute()?;
// List all symlinks
let symlinks = repo.fs().list_symlinks()?;
for (link, target) in symlinks {
println!("{} -> {}", link, target);
}
```
### Write Operations
```rust
repo.fs().modify()
.message("Update configuration")
.author("developer")
.write_str("VERSION", "1.0.0\n")
.write("data.bin", &[0x00, 0x01, 0x02])
.touch(".gitkeep") // Create empty file
.execute()?;
```
### Preview Without Committing
```rust
let preview = repo.fs().modify()
.copy_file("a.txt", "b.txt")
.delete_dir("old")
.preview()?;
println!("Base commit: {}", preview.base_commit);
println!("Files in base: {}", preview.base_file_count);
for desc in preview.describe() {
println!(" {}", desc);
}
// Output:
// COPY a.txt -> b.txt
// DELETE old/ (recursive)
```
### Advanced Find
```rust
// Find with ignore patterns
let files = repo.fs().find()
.pattern("**/*.rs")
.ignore("target/**")
.ignore("**/generated/**")
.ignore_hidden() // Exclude .* files
.paths()?;
// Find with common gitignore patterns
let clean = repo.fs().find()
.pattern("**/*")
.use_gitignore() // Excludes node_modules, target, .git, etc.
.paths()?;
// Find in specific directory with depth limit
let shallow = repo.fs().find()
.in_dir("src")
.pattern("**/*.rs")
.max_depth(2)
.paths()?;
// Find at specific commit/branch/tag
let files = repo.fs().find()
.on_branch("feature-x")?
.pattern("**/*.rs")
.paths()?;
// Get full results with metadata
let result = repo.fs().find()
.pattern("**/*.rs")
.execute()?;
println!("Found {} files in {} directories", result.count, result.dirs_traversed);
for file in result.files {
println!(" {} ({:?} bytes)", file.path, file.size);
}
// Just count matches
let count = repo.fs().find().pattern("**/*.rs").count()?;
```
### Utility Functions
```rust
// Check if file exists
let exists = repo.fs().exists("README.md")?;
// Check if path is a directory
let is_dir = repo.fs().is_dir("src")?;
// Get file metadata
if let Some(info) = repo.fs().stat("README.md")? {
println!("Path: {}", info.path);
println!("Hash: {}", info.hash);
println!("Size: {:?} bytes", info.size);
}
// Calculate total size of matching files
let total_size = repo.fs().du("**/*.rs")?;
// Count matching files
let file_count = repo.fs().count("**/*.rs")?;
```
## User Management
Access via `repo.users()`:
```rust
// List all users
let users = repo.users().list()?;
for (login, caps) in users {
println!("User: {} (capabilities: {})", login, caps);
}
// Create a new user
repo.users()
.create("developer")
.password("secret123")
.capabilities("ei") // edit + check-in
.execute()?;
// Get user reference
let user = repo.users().get("developer")?;
println!("Login: {}", user.login());
if let Some(caps) = user.capabilities()? {
println!("Capabilities: {}", caps);
}
// Update user capabilities
user.set_capabilities("aei")?; // admin + edit + check-in
```
### Capability Codes
| `a` | Admin |
| `d` | Delete |
| `e` | Email |
| `i` | Check-in |
| `o` | Check-out |
| `r` | Read |
| `v` | Developer |
| `w` | Write ticket |
## Sync Operations (QUIC)
Requires the `sync-quic` feature:
```toml
heroforge = { version = "0.2.1", features = ["sync-quic"] }
```
Access via `repo.sync()`:
```rust
// Push to remote
repo.sync()
.to("quic://server.example.com:4443/repo")
.push()?;
// Pull from remote
repo.sync()
.from("quic://server.example.com:4443/repo")
.pull()?;
// With authentication
repo.sync()
.to("quic://server.example.com:4443/repo")
.auth("username", "password")
.push()?;
// Bidirectional sync
let result = repo.sync()
.with("quic://server.example.com:4443/repo")
.execute()?;
println!("Sent: {} artifacts", result.artifacts_sent);
println!("Received: {} artifacts", result.artifacts_received);
```
## Server Operations (QUIC)
Requires the `sync-quic` feature:
```rust
use heroforge::{Repository, Server};
let repo = Repository::open("project.fossil")?;
// Start QUIC server (blocking)
Server::quic()
.bind("0.0.0.0:4443")
.start_blocking(&repo)?;
```
## Git Import
Import a git repository into heroforge (requires `git-import` feature).
### Installation
```toml
[dependencies]
heroforge = { version = "0.2.1", features = ["git-import"] }
```
### Basic Import
```rust
use heroforge::Repository;
// Create a new heroforge repository
let repo = Repository::init("project.fossil")?;
// Import from a git repository (default branch)
repo.git_import()
.url("https://github.com/user/project.git")
.message("Import from git repository")
.author("developer")
.execute()?;
```
### Import from Branch, Tag, or Commit
```rust
// Import from a specific branch
repo.git_import()
.url("https://github.com/user/project.git")
.branch("main")
.message("Import main branch")
.author("developer")
.execute()?;
// Import from a tag
repo.git_import()
.url("https://github.com/user/project.git")
.tag("v1.0.0")
.message("Import v1.0.0 release")
.author("developer")
.execute()?;
// Import from a specific commit
repo.git_import()
.url("https://github.com/user/project.git")
.commit_hash("abc123def456")
.message("Import specific commit")
.author("developer")
.execute()?;
```
### Import with File Filtering
```rust
// Include only specific files
repo.git_import()
.url("https://github.com/user/project.git")
.branch("main")
.include_pattern("src/**/*.rs")
.include_pattern("Cargo.toml")
.include_pattern("README.md")
.message("Import Rust source files only")
.author("developer")
.execute()?;
// Exclude files
repo.git_import()
.url("https://github.com/user/project.git")
.branch("main")
.exclude_pattern("**/test/**")
.exclude_pattern("**/*.md")
.exclude_pattern("**/node_modules/**")
.message("Import without tests and docs")
.author("developer")
.execute()?;
// Combine include and exclude
repo.git_import()
.url("https://github.com/user/project.git")
.branch("main")
.include_pattern("**/*.rs")
.exclude_pattern("**/test/**")
.message("Import Rust files except tests")
.author("developer")
.execute()?;
```
### Import to a Specific Branch
```rust
// Import into a new branch
repo.git_import()
.url("https://github.com/user/project.git")
.branch("main")
.to_branch("imported-code")
.message("Import from external git repo")
.author("developer")
.execute()?;
```
### Git Import Builder Methods
| `.url(url)` | Git repository URL (HTTPS or SSH) |
| `.branch(name)` | Checkout specific branch |
| `.tag(name)` | Checkout specific tag |
| `.commit_hash(hash)` | Checkout specific commit |
| `.message(msg)` | Commit message for the import |
| `.author(user)` | Author of the import commit |
| `.include_pattern(glob)` | Include files matching pattern |
| `.exclude_pattern(glob)` | Exclude files matching pattern |
| `.to_branch(name)` | Target branch in heroforge |
| `.parent(hash)` | Parent commit hash |
| `.execute()` | Execute the import, returns commit hash |
## Complete Example
```rust
use heroforge::Repository;
fn main() -> heroforge::Result<()> {
// Create a new repository
let repo = Repository::init("project.fossil")?;
// Create initial commit
let init = repo.commit()
.message("Initial commit")
.author("admin")
.initial()
.execute()?;
// Add project files
let v1 = repo.commit()
.message("Add project structure")
.author("developer")
.parent(&init)
.file("README.md", b"# My Project\n\nA sample project.\n")
.file("src/lib.rs", b"pub fn hello() -> &'static str { \"Hello!\" }\n")
.file("src/main.rs", b"fn main() { println!(\"{}\", myproject::hello()); }\n")
.file("Cargo.toml", b"[package]\nname = \"myproject\"\nversion = \"1.0.0\"\n")
.execute()?;
// Tag the release
repo.tags()
.create("v1.0.0")
.at_commit(&v1)
.author("developer")
.execute()?;
// Create a feature branch
repo.branches()
.create("feature-auth")
.from_commit(&v1)
.author("developer")
.execute()?;
// Work on the feature branch
let _feature = repo.commit()
.message("Add authentication module")
.author("developer")
.parent(&v1)
.branch("feature-auth")
.file("src/auth.rs", b"pub fn login(user: &str, pass: &str) -> bool { true }\n")
.execute()?;
// Read files from different locations
let readme = repo.files().on_trunk().read_string("README.md")?;
println!("README:\n{}", readme);
let auth = repo.files().on_branch("feature-auth").read_string("src/auth.rs")?;
println!("Auth module:\n{}", auth);
let v1_readme = repo.files().at_tag("v1.0.0").read_string("README.md")?;
println!("v1.0.0 README:\n{}", v1_readme);
// Find all Rust files on trunk
let rust_files = repo.files().on_trunk().find("**/*.rs")?;
println!("\nRust files:");
for file in rust_files {
println!(" {} ({:?} bytes)", file.name, file.size);
}
// Get recent history
let recent = repo.history().recent(5)?;
println!("\nRecent commits:");
for commit in recent {
println!(" {} | {} | {}", &commit.hash[..12], commit.user, commit.comment);
}
// List branches and tags
let branches = repo.branches().list()?;
println!("\nBranches: {:?}", branches);
let tags = repo.tags().list()?;
println!("Tags: {:?}", tags);
Ok(())
}
```
## Error Handling
All operations return `heroforge::Result<T>`:
```rust
use heroforge::{Repository, FossilError, Result};
fn example() -> Result<()> {
let repo = Repository::open("project.fossil")?;
match repo.files().on_trunk().read("missing.txt") {
Ok(content) => println!("Content: {} bytes", content.len()),
Err(FossilError::FileNotFound(path)) => println!("File not found: {}", path),
Err(e) => return Err(e),
}
Ok(())
}
```
## Types Reference
### FileInfo
Returned by file listing operations:
```rust
pub struct FileInfo {
pub name: String, // File path
pub hash: String, // Content hash
pub size: Option<usize>, // File size in bytes
pub permissions: Option<String>, // Unix permissions (octal)
}
```
### CheckIn
Returned by history operations:
```rust
pub struct CheckIn {
pub hash: String, // Commit hash
pub user: String, // Author
pub comment: String, // Commit message
pub timestamp: String, // ISO 8601 timestamp
}
```
### SyncResult
Returned by sync operations:
```rust
pub struct SyncResult {
pub artifacts_sent: usize,
pub artifacts_received: usize,
pub protocol: SyncProtocol,
}
```
## Feature Flags
| (default) | Core read/write operations | None |
| `sync-quic` | QUIC protocol sync | `quinn`, `rustls`, `tokio` |
| `git-import` | Import git repositories into heroforge | `herolib-os` |
| `herolib` | Full herolib integration (core + os) | `herolib-core`, `herolib-os` |
## Herolib Integration
Heroforge integrates with the **herolib** family of crates for extended functionality. These are our own libraries that provide system utilities and should be used where applicable.
### herolib-core
Core utilities for text processing, networking, and configuration.
```toml
[dependencies]
heroforge = { version = "0.2.1", features = ["herolib"] }
```
**Modules:**
- `text` - Text processing utilities (regex, templating, normalization)
- `net` - Network connectivity utilities (TCP, HTTP, SSH checks)
- `heroscript` - HeroScript configuration language for safe system orchestration
**Example:**
```rust
#[cfg(feature = "herolib")]
use herolib_core::{text, net};
// Text utilities
let result = text::replace("hello world", "world", "rust");
// Network checks
let is_up = net::tcp_check("localhost", 8080);
```
### herolib-os
System utilities for OS interaction, process management, virtualization, git, and Kubernetes.
**Modules:**
- `os` - Filesystem operations, downloads, packages, platform detection
- `process` - Cross-platform process management and command execution
- `git` - Git repository management and operations
- `virt` - Virtualization tools (Buildah, Nerdctl, RFS, QCOW2, Cloud Hypervisor)
- `installers` - Software installation utilities
- `kubernetes` - Kubernetes cluster management (requires `kubernetes` feature in herolib-os)
**Example:**
```rust
#[cfg(feature = "git-import")]
use herolib_os::{os, process, git};
// OS utilities
let home = os::home();
// Run a command
let result = process::run_command("echo hello");
// Work with git repositories
let tree = git::GitTree::new("/path/to/repos");
```
### When to Use Herolib
Use herolib crates instead of implementing custom solutions for:
| Text manipulation (regex, templates) | `herolib_core::text` |
| Network connectivity checks | `herolib_core::net` |
| Configuration parsing | `herolib_core::heroscript` |
| File/directory operations | `herolib_os::os` |
| Running shell commands | `herolib_os::process` |
| Git repository operations | `herolib_os::git` |
| Container/VM management | `herolib_os::virt` |
| Installing software | `herolib_os::installers` |
| Kubernetes operations | `herolib_os::kubernetes` |
## Documentation
- [API Documentation](https://docs.rs/heroforge)
- [Crates.io](https://crates.io/crates/heroforge)
- [Repository](https://github.com/herocode/heroforge)
Generate local documentation:
```bash
cargo doc --open --all-features
```
## Examples
Run built-in examples:
```bash
# Basic repository reading
cargo run --example read_repo
# Find files with glob patterns
cargo run --example find_and_read
# Branches and tags
cargo run --example branch_tag_test
# Comprehensive API demo
cargo run --example comprehensive_test
# Filesystem operations
cargo run --example fs_operations
# Advanced find
cargo run --example fs_find
# QUIC sync (requires feature)
cargo run --example quic_simple_test --features sync-quic
```