libvctrl_handler
Fundamental contracts for building a version control system — no implementations, only
traits, types, and validation. libvctrl_handler is the "constitution" layer of the
libvctrl ecosystem: it defines what a VCS object model looks like without prescribing
how it is stored, serialised, signed, or transported. Every other crate in the workspace
implements or consumes these contracts.
- Crate:
libvctrl_handler5.2.0 (library,std-only, zero external runtime dependencies) - Language: Rust, edition 2024 — MSRV 1.96.0
- License: MIT
- Repository: https://github.com/mroczect/libvctrl
- Documentation: https://docs.rs/libvctrl_handler
Most users should depend on the
libvctrlfacade, which re-exports this crate alongside the reference implementation and the crypto primitives. Reach forlibvctrl_handlerdirectly only when you are implementing a custom backend or need the contract surface without the reference implementation.
Overview
libvctrl_handler is a pure contracts crate. It contains no concrete implementations —
no storage backends, no serializers, no hashing engines. Instead it provides:
- Traits defining every major VCS operation: object storage, encoding, decoding, hashing, indexing, reference management, reflogs, remote transport, packing, signing, verification, revision walking, diffing, blame, and configuration.
- Immutable data types representing Git objects (
Blob,Tree,Commit,Tag) and supporting structures (Hash,UserID,TreeEntry,CommitMeta, deltas, merges, reflog entries). - Constants enforcing safe size and count limits to prevent resource exhaustion.
- Validation functions that define the security boundary for names, references, tree entries, and hashes.
- A unified error type (
VctrlError) that all trait implementations must return.
The crate depends only on the Rust standard library. It is std-only and exposes no
crate-level feature flags. There is no no_std build path at present.
Architecture
libvctrl_handler is the foundation of a strictly layered, one-way dependency graph. It
sits at the bottom; the reference implementation (libvctrl_core) implements its traits;
the facade (libvctrl) re-exports both; and the command crates (libvctrl_plumbing,
libvctrl_porcelain) build on top.
flowchart TD
subgraph Apps["Application layer"]
FACADE["libvctrl<br/>facade (re-exports)"]
PL["libvctrl_plumbing"]
PO["libvctrl_porcelain"]
end
subgraph Ref["Reference implementation"]
CORE["libvctrl_core<br/>codec, builders, stores, hasher"]
end
subgraph Contracts["Contract layer — libvctrl_handler (this crate)"]
HANDLER["traits + types + constants<br/>enums + errors + macros + validation<br/>std-only, zero dependencies"]
end
subgraph Crypto["Cryptography"]
SHA["libvctrl_sha512<br/>raw SHA-512 engine"]
end
FACADE --> HANDLER
FACADE --> CORE
FACADE --> SHA
PL --> CORE
PO --> CORE
CORE --> HANDLER
CORE --> SHA
Internal module organization
The crate is separated into seven modules, each with a single responsibility. The crate
root (lib.rs) re-exports the most commonly used items for ergonomic access.
flowchart LR
LIB["lib.rs<br/>crate root + root re-exports"]
LIB --> CONST["constants<br/>limits + entry_mode"]
LIB --> ENUMS["enums<br/>EntryKind"]
LIB --> ERRORS["errors<br/>VctrlError"]
LIB --> MACROS["macros<br/>vctrl_error_other!"]
LIB --> TRAITS["traits/core<br/>17 contracts"]
LIB --> TYPES["types/core<br/>14 immutable data types"]
LIB --> VAL["validation<br/>4 input checks"]
VAL -.gates construction.-> TYPES
TRAITS -.consume / return.-> TYPES
TYPES -.return.-> ERRORS
TRAITS -.return.-> ERRORS
ENUMS -.used by.-> TYPES
CONST -.used by.-> TYPES
CONST -.used by.-> VAL
Invalid states are unrepresentable
A core design idiom is that all public data types are constructed via fallible
constructors that enforce invariants at construction time. Once built, objects are
immutable and expose only &self accessors. This pushes validation to the boundary of the
system and guarantees that invalid objects cannot exist at runtime.
flowchart LR
INPUT[Raw input<br/>name / hash bytes / ref name / tree entry]
INPUT --> VALFN[validation function<br/>validate_name / validate_hash_bytes / ...]
VALFN -->|invalid| ERR1[VctrlError<br/>InvalidName / InvalidHashLength / ...]
VALFN -->|valid| CTOR[Fallible constructor<br/>Blob::new / Tree::new / UserID::new / ...]
CTOR -->|invariant violated| ERR2[VctrlError]
CTOR -->|ok| OBJ[Immutable validated object<br/>only &self accessors]
OBJ -.Send + Sync.-> THREADS[Cross-thread consumers]
Core Features
- Pure contracts. No implementations — only traits, types, constants, and validation. Backend authors implement the traits; tool authors depend on the types.
- Invalid states unrepresentable. All data types use fallible constructors that reject malformed input at construction time; objects are immutable thereafter.
- Resource-exhaustion prevention. Hard
MAX_*limits act as fail-fast circuit breakers during construction, bounding memory allocation against malicious input. - Strong typing over raw integers. Git mode bits are represented by the
EntryKindenum withconst fnconversions to and from rawu32modes, preventing invalid combinations at compile time. - Forward-compatible enums and errors.
EntryKindandVctrlErrorare#[non_exhaustive], allowing new variants without breaking API compatibility. - Thread-safe contracts. Traits carry
Send + Syncbounds so backends can be shared across threads;&mut selfis required only for write operations. - Cloneable I/O errors.
std::io::Erroris wrapped inArcinsideVctrlErrorso the error remainsClonedespiteio::Errornot beingClone. - Compile-time computation.
const fnis used where possible (EntryKind::mode,from_mode) to shift work to compile time. - Strict safety.
#![forbid(unsafe_code)]and a comprehensive set of denied Clippy and documentation lints are inherited from the workspace.
Technology Stack
- Language: Rust (edition 2024, MSRV 1.96.0)
- Dependencies: none (standard library only)
- Dev-dependencies:
criterion0.8 (withcargo_bench_supportfeature) - Lint policy: workspace-inherited,
#![forbid(unsafe_code)], deniedmissing_docs,rust_2018_idioms, and a broad set of Clippy lints (includingpedanticandnurserygroups). See the repository for the authoritative lint configuration. - Feature flags: none.
Note on the MSRV field: this crate's
Cargo.tomldoes not yet declarerust-version = "1.96". The workspace standard is Rust 1.96.0, and this README documents that as the MSRV. Addingrust-version = "1.96"tolibvctrl_handler/Cargo.tomlis recommended in a future maintenance pass.
Project Structure
libvctrl_handler/
├── Cargo.toml
├── benches/
│ └── handler_bench.rs
└── src/
├── lib.rs
├── constants.rs
├── errors.rs
├── macros.rs
├── enums/
│ ├── mod.rs
│ └── core/
│ ├── mod.rs
│ └── entry_kind.rs
├── traits/
│ ├── mod.rs
│ └── core/
│ ├── mod.rs
│ ├── blame.rs
│ ├── config.rs
│ ├── decoder.rs
│ ├── diff.rs
│ ├── encoder.rs
│ ├── hasher.rs
│ ├── index.rs
│ ├── object_store.rs
│ ├── pack.rs
│ ├── ref_store.rs
│ ├── reflog.rs
│ ├── remote.rs
│ ├── revwalk.rs
│ ├── signer.rs
│ ├── transport.rs
│ └── verifier.rs
├── types/
│ ├── mod.rs
│ └── core/
│ ├── mod.rs
│ ├── blob.rs
│ ├── commit.rs
│ ├── delta.rs
│ ├── hash.rs
│ ├── merge.rs
│ ├── reflog.rs
│ ├── tag.rs
│ ├── tree.rs
│ └── user_id.rs
└── validation/
├── mod.rs
├── hash.rs
└── name.rs
Getting Started
Prerequisites
- Rust toolchain 1.96.0 or newer (edition 2024 is required)
- Cargo
No system libraries or external services are required.
Installation
For most users, depend on the facade instead:
[]
= "2.2"
To depend on libvctrl_handler directly (contracts only, no reference implementation):
[]
= "5.2"
Or via Cargo:
Because the crate has no external dependencies, adding it is essentially zero-weight beyond the standard library.
Configuration
libvctrl_handler exposes no crate-level feature flags and requires no runtime
configuration. The contract surface is fixed; behavioural configuration (such as the
sha384 / opt_size crypto features) lives at the facade or libvctrl_sha512 level.
Usage
Create a Hash and inspect an EntryKind
use ;
// Hash requires exactly 64 bytes (SHA-512).
let raw_bytes = ;
let hash = from_bytes;
assert!;
// Git object modes are accessible via the strongly-typed EntryKind enum.
let blob_mode = Blob.mode;
assert_eq!;
Convert between EntryKind and raw mode bits
use EntryKind;
// Enum -> raw mode (const fn, usable in const contexts).
assert_eq!;
// Raw mode -> Enum (graceful on invalid input).
assert_eq!;
assert_eq!;
Construct ad-hoc errors with the macro
use ;
let err = vctrl_error_other!;
assert_eq!;
Wrap an I/O error
use VctrlError;
use ;
let io_err = new;
let vctrl_err = from_io;
// The error is cloneable despite wrapping std::io::Error.
let cloned = vctrl_err.clone;
assert_eq!;
Implement a trait for a custom backend
The traits are designed to be implemented by backend authors. The Blame trait
illustrates the idiom: Send + Sync bounds for thread safety, &self for read
operations, and VctrlError as the unified failure type.
use ;
use ;
;
let repo = MockRepo;
let entries = repo.blame_file.unwrap;
assert_eq!;
API Reference / Core Modules
Full API documentation is published at https://docs.rs/libvctrl_handler. The summary below describes each module and its public surface.
constants — Limits and protocol values
Centralises all magic numbers so that limits are uniformly enforced at the type construction level. Two categories: resource-exhaustion circuit breakers and Git protocol constants.
| Constant | Value | Purpose |
|---|---|---|
HASH_LENGTH |
64 |
SHA-512 hash length in bytes; enables fixed-size arrays. |
MAX_NAME_LENGTH |
255 |
Upper bound on file/directory/reference names (bytes). |
MAX_BLOB_SIZE |
100 * 1024^2 |
Maximum blob size (100 MiB); DoS circuit breaker. |
MAX_TREE_ENTRIES |
100_000 |
Maximum entries per tree; bounds parse/diff cost. |
MAX_MESSAGE_LENGTH |
1024 * 1024 |
Maximum commit/tag message length (1 MiB). |
MAX_PARENT_COUNT |
0xFFFF |
Maximum parent commits (u16 range). |
The entry_mode submodule exposes the Git protocol mode bits:
| Constant | Value | Meaning |
|---|---|---|
BLOB |
0o100_644 |
Regular file |
EXECUTABLE |
0o100_755 |
Executable file |
SYMLINK |
0o120_000 |
Symbolic link |
TREE |
0o40_000 |
Directory (tree) |
SUBMODULE |
0o160_000 |
Submodule commit |
enums — Strongly-typed protocol kinds
EntryKind— a#[non_exhaustive]enum (Blob,Executable,Symlink,Tree,Submodule) classifying tree entries. Providesconst fn mode() -> u32andconst fn from_mode(u32) -> Option<Self>for lossless conversion to and from raw Git mode bits. Consumers must include a_catch-all when matching.
errors — Unified error type
VctrlError— a#[non_exhaustive],Clone + Debug + PartialEq + Eqenum that all fallible operations across the ecosystem return. ImplementsDisplayandstd::error::Error. I/O errors are stored asArc<std::io::Error>to make the errorClone. A manualPartialEqcomparesio::Error::kind()and string representation.
| Variant | Meaning |
|---|---|
CorruptedData(String) |
Data was corrupted or malformed. |
DuplicateParent |
A commit contains duplicate parent hashes. |
ExceededMaxSize(String) |
A size or count limit was exceeded. |
InvalidBlameRange |
An invalid blame range was specified (zero start or count). |
InvalidEmail(String) |
An email address was invalid. |
InvalidHashLength(usize) |
Hash length did not match HASH_LENGTH. |
InvalidName(String) |
A name was empty, too long, or contained control characters. |
InvalidTimezoneOffset(i16) |
Timezone offset outside -1440..=1440. |
InvalidTreeStructure(String) |
Tree entries unsorted or duplicated. |
IoError(Arc<io::Error>) |
An I/O error occurred. |
ObjectNotFound(Hash) |
An object with the given hash was not found. |
Other(String) |
Any error not covered by the above. |
RefNotFound(String) |
A reference with the given name was not found. |
SerializationError(String) |
A serialization/deserialization error occurred. |
VctrlError::from_io(io::Error) is the canonical constructor for I/O failures.
macros — Error-construction helpers
vctrl_error_other!— an exported declarative macro that wrapsformat!intoVctrlError::Other. Uses$cratefor absolute path resolution so the macro remains correct even when imported via glob from another crate.
traits — The 17 backend contracts
All traits live under traits::core and are re-exported at the crate root. They are
grouped by domain:
Serialization and content addressing
| Trait | Purpose |
|---|---|
Encoder |
Serialise objects (Blob, Tree, Commit, Tag) to bytes. |
Decoder |
Parse bytes back into validated objects; the trust boundary. |
Hasher |
Compute a content-address Hash from a byte stream. |
Object and reference storage
| Trait | Purpose |
|---|---|
ObjectStore |
Store and retrieve encoded object bytes keyed by Hash. |
RefStore |
Manage named references (branches, tags) pointing at hashes. |
ReflogStore |
Append and read reflog entries for a reference. |
Index |
Manage the staged index of path-to-hash mappings. |
History traversal and analysis
| Trait | Purpose |
|---|---|
RevWalk |
Traverse the commit graph from a set of heads. |
Blame |
Attribute file line ranges to commits (BlameEntry). |
TreeDiffer |
Compute tree-level deltas (TreeDelta, FileDelta, ChangeKind). |
Remote and packfile
| Trait | Purpose |
|---|---|
Transport |
Abstract network transport for remote operations. |
Remote |
Manage remote configuration and endpoint interaction. |
PackReader |
Decode Git packfile streams into objects. |
PackWriter |
Encode objects into Git packfile streams. |
Cryptographic integrity
| Trait | Purpose |
|---|---|
Signer |
Produce cryptographic signatures over objects. |
Verifier |
Verify cryptographic signatures over objects. |
Configuration
| Trait | Purpose |
|---|---|
ConfigStore |
Read and write sectioned configuration values (Option-returning reads for sparse keys; &mut self for writes). |
All traits require Send + Sync. Write operations take &mut self; read operations take
&self. BlameEntry is the companion struct for Blame, constructed via a fallible
new() that rejects zero start lines or counts.
types — The 14 immutable data types
All types live under types/core and are re-exported at the crate root. Each is immutable
after construction and built through a fallible constructor.
| Type | Purpose |
|---|---|
Blob |
A file's raw content, bounded by MAX_BLOB_SIZE. |
Tree |
A sorted collection of TreeEntry objects, bounded by MAX_TREE_ENTRIES. |
TreeEntry |
A single directory entry: name, EntryKind, and target Hash. |
Commit |
A commit object: tree hash, parents, author/committer, message, metadata. |
CommitMeta |
Timestamp, timezone offset, and optional encoding for a commit. |
Tag |
An annotated tag: name, target hash, optional tagger, message, metadata. |
Hash |
A fixed 64-byte SHA-512 content address (Copy, Send, Sync). |
UserID |
A name and email pair with validation. |
ReflogEntry |
A single reflog record (old/new hash, committer, message). |
ChangeKind |
The kind of change in a diff (added, modified, deleted, etc.). |
FileDelta |
A per-file change between two trees. |
TreeDelta |
An aggregate tree-level diff. |
Conflict |
A merge conflict representation. |
MergeResult |
The outcome of a merge operation. |
validation — Input checks
Pure functions that define the security boundary for raw inputs, intended to be applied before attempting object construction so that malformed data fails fast.
| Function | Validates |
|---|---|
validate_hash_bytes |
That a byte slice is exactly HASH_LENGTH bytes. |
validate_name |
That a name is non-empty, within MAX_NAME_LENGTH, and free of control characters. |
validate_ref_name |
That a reference name is structurally valid (e.g. refs/heads/main). |
validate_tree_entry_name |
That a tree entry name is a valid path component. |
Testing
Run the crate's test suite with Cargo:
The crate includes unit tests for constructors, validation, EntryKind conversions, and
tree sorting/duplicate detection. To run the entire workspace test suite from the
repository root:
To run the benchmark suite:
To verify the strict lint policy is satisfied:
Contributing
Contributions are welcome. The crate enforces #![forbid(unsafe_code)] and inherits a
strict, denied Clippy and documentation-lint policy from the workspace; all public items
must be documented.
For contribution guidelines, code style, and the full lint configuration, see the
repository's CONTRIBUTING.md and the workspace root README.md:
- Repository: https://github.com/mroczect/libvctrl
When contributing to libvctrl_handler, preserve the contracts-only invariant: no
concrete implementations belong here. New behaviours belong in libvctrl_core; new
contracts and types belong here; new user-facing commands belong in libvctrl_plumbing
or libvctrl_porcelain.
Ecosystem
libvctrl_handler is the foundation of a larger workspace. The related crates are listed
below; each has its own documentation.
| Crate | Role | Documentation |
|---|---|---|
libvctrl |
Facade: re-exports contracts, reference impl, and crypto | https://docs.rs/libvctrl |
libvctrl_core |
Reference implementations of these contracts | https://docs.rs/libvctrl_core |
libvctrl_sha512 |
Zero-dependency SHA-512 / HMAC / HKDF primitives | https://docs.rs/libvctrl_sha512 |
libvctrl_plumbing |
Command-level VCS operations built on libvctrl_core |
https://docs.rs/libvctrl_plumbing |
libvctrl_porcelain |
High-level, user-facing VCS operations | https://docs.rs/libvctrl_porcelain |
The dependency flow is strictly one-way: libvctrl_handler is the foundation,
libvctrl_core implements its contracts, the facade re-exports both, and
libvctrl_plumbing / libvctrl_porcelain build on libvctrl_core.
flowchart LR
H[libvctrl_handler<br/>contracts] --> C[libvctrl_core<br/>reference impl]
C --> PL[libvctrl_plumbing]
C --> PO[libvctrl_porcelain]
H --> F[libvctrl<br/>facade]
C --> F
License
Licensed under the MIT License. See the repository for the full license text.