# Heroforge
A pure Rust library for version-controlled storage with a filesystem-like API.
## Quick Start
```rust
use heroforge::Repository;
// Create a new repository
let repo = Repository::init("project.forge")?;
// Initial commit
let hash = repo.commit()
.message("Initial commit")
.author("developer")
.initial()
.execute()?;
// Add files
repo.commit()
.message("Add project files")
.author("developer")
.parent(&hash)
.file("README.md", b"# My Project")
.file("src/main.rs", b"fn main() {}")
.execute()?;
// Read files
let content = repo.files().on_trunk().read_string("README.md")?;
```
## Two Ways to Work with Files
### 1. Repository API (Immediate Commits)
Each operation creates a commit immediately:
```rust
let repo = Repository::open_rw("project.forge")?;
// Single commit with multiple files
repo.commit()
.message("Update files")
.author("developer")
.parent(&last_hash)
.file("config.json", b"{}")
.file("data.txt", b"hello")
.execute()?;
// Filesystem operations (copy, move, delete)
repo.fs().modify()
.message("Reorganize")
.author("developer")
.copy_file("README.md", "docs/README.md")
.delete_file("old.txt")
.execute()?;
```
### 2. FsInterface (Staging with Background Commits)
Writes go to a staging directory first, then auto-commit every minute:
```rust
use heroforge::{Repository, FsInterface};
use std::sync::Arc;
let repo = Arc::new(Repository::open_rw("project.forge")?);
let fs = FsInterface::new(repo, "developer")?;
// Fast writes to staging
fs.write_file("config.json", b"{}")?;
fs.write_file("data.txt", b"hello")?;
// Reads check staging first, then database
let content = fs.read_file("config.json")?;
// Partial updates (efficient for large files)
fs.write_at("data.bin", 100, b"updated")?;
// Force immediate commit (or wait for auto-commit)
fs.commit()?;
```
**When to use which:**
- **Repository API**: Explicit control, each operation is a commit
- **FsInterface**: High-frequency writes, automatic batching, filesystem-like usage
## Common Operations
### Branches
```rust
// List
let branches = repo.branches().list()?;
// Create
repo.branches()
.create("feature")
.from_branch("trunk")
.author("developer")
.execute()?;
// Get latest commit
let tip = repo.branches().get("feature")?.tip()?;
```
### Tags
```rust
// Create at branch tip
repo.tags()
.create("v1.0.0")
.at_branch("trunk")
.author("developer")
.execute()?;
// Create at specific commit
repo.tags()
.create("v1.0.1")
.at_commit(&hash)
.author("developer")
.execute()?;
// List
let tags = repo.tags().list()?;
```
### Reading Files
```rust
// From trunk
let bytes = repo.files().on_trunk().read("file.bin")?;
let text = repo.files().on_trunk().read_string("file.txt")?;
// From branch or tag
let content = repo.files().on_branch("feature").read("src/lib.rs")?;
let content = repo.files().at_tag("v1.0.0").read("Cargo.toml")?;
// List and find
let files = repo.files().on_trunk().list()?;
let rust_files = repo.files().on_trunk().find("**/*.rs")?;
```
### Filesystem Operations
```rust
use heroforge::fs::{Find, Modify};
// Find files with patterns
let rust_files = Find::new(&repo)
.pattern("**/*.rs")
.ignore("target/**")
.ignore_hidden()
.max_depth(5)
.paths()?;
// Convenience functions
use heroforge::fs::{find, count, exists, is_dir, stat, du};
let files = find(&repo, "**/*.rs")?;
let num = count(&repo, "**/*.md")?;
let has_readme = exists(&repo, "README.md")?;
let total_size = du(&repo, "src/**/*")?;
// Batch operations (single commit)
Modify::new(&repo)
.message("Cleanup")
.author("developer")
.copy_file("a.txt", "backup/a.txt")
.move_file("old.txt", "archive/old.txt")
.delete_dir("temp")
.make_executable("scripts/run.sh")
.symlink("latest", "releases/v1.0.0")
.execute()?;
```
### Upload/Download (OS Filesystem <-> Repository)
```rust
use heroforge::fs::{upload, upload_dir, download, download_dir, download_matching};
// Upload file from OS to repository
upload(&repo, "/path/to/local/file.txt", "file.txt", "developer", None)?;
// Upload entire directory
upload_dir(&repo, "/path/to/local/src", "src", "developer", Some("Import source"))?;
// Download file from repository to OS
download(&repo, "config.json", "/tmp/config.json")?;
// Download entire directory
let count = download_dir(&repo, "src", "/tmp/src_export")?;
// Download matching files
let count = download_matching(&repo, "**/*.rs", "/tmp/rust_files")?;
```
### History
```rust
// Recent commits
let commits = repo.history().limit(10).list()?;
for commit in commits {
println!("{} - {}", commit.hash, commit.comment);
}
// Get specific commit
let commit = repo.history().get(&hash)?;
```
### Sync (Push/Pull)
```rust
// Push to remote
repo.sync()
.to("quic://server:4443/repo")
.push()?;
// Pull from remote
repo.sync()
.from("quic://server:4443/repo")
.auth("user", "password")
.pull()?;
```
## Error Handling
All operations return `Result<T, FossilError>`:
```rust
use heroforge::{Repository, FossilError};
match repo.files().on_trunk().read("missing.txt") {
Ok(content) => println!("Got {} bytes", content.len()),
Err(FossilError::ArtifactNotFound(_)) => println!("File not found"),
Err(e) => println!("Error: {}", e),
}
```
## Features
Enable optional features in `Cargo.toml`:
```toml
[dependencies]
heroforge = { version = "0.2", features = ["git-import"] }
```
### git-import
Import from Git repositories:
```rust
repo.git_import()
.url("https://github.com/user/project.git")
.branch("main")
.message("Import from git")
.author("developer")
.execute()?;
```
## Module Overview
| `repo` | Repository operations, commits, branches, tags |
| `fs` | Filesystem interface with staging |
| `sync` | Push/pull over QUIC protocol |
| `artifact` | Low-level blob and manifest handling |
| `tools` | Import utilities (git-import) |