type-lib 1.0.0

Validation and type constraint library. Declare domain types with invariants enforced at construction. Parse-dont-validate pattern as a first-class citizen. Zero-overhead wrappers with derive macros.
Documentation

What it does

type-lib makes invalid states unrepresentable. Instead of validating a value every time it is used, you validate it once — at construction — and carry a type that the compiler will only let exist in a valid state. Functions that take such a type are freed from defensive checks: the type system already did them.

The foundation is two pieces that compose:

  • Validator — a reusable, type-level validation rule. It lives on a zero-sized marker type and is selected through the type system, so it carries no state and no storage.
  • Refined — a #[repr(transparent)] wrapper holding a value proven to satisfy a Validator. It has the exact size and layout of the value it wraps, so the guarantee is free at runtime.

A ready-made ValidationError covers rules that need only a code and a message; rules that need structured failures define their own error type through Validator::Error.


Features

  • Parse, don't validate — invariants are enforced at construction and proven by the type thereafter; no re-checking at call sites.
  • Zero-overhead wrappersRefined<T, V> is #[repr(transparent)] over T and stores nothing extra. The validated type is the same size as the raw one.
  • Built-in rules — ready-made validators for length (NonEmpty, MinLen, MaxLen, LenRange), numbers (Positive, InRange, …), and string content (Ascii, Alphanumeric, Trimmed).
  • Composition — combine rules at the type level with And, Or, and Not; the result is itself a rule.
  • Derive macro#[derive(Validated)] (the derive feature) turns a newtype into a named domain type with a checked constructor.
  • Reusable, type-level rules — write a Validator once and apply it to any value type through the type system.
  • Tamper-proof by constructionRefined exposes no &mut to its inner value and no public field, so a validated value cannot be mutated into an invalid one behind the type's back.
  • Bring your own error — use the bundled ValidationError or any custom error type via the Validator::Error associated type.
  • no_std friendly — the core API and all borrowed-value rules work without std; alloc adds owned-type (String / Vec) length rules and std adds the [std::error::Error] impl.
  • Cross-platform — Linux, macOS, and Windows on stable and MSRV 1.75.

Performance

Validation is the only runtime cost; the wrapper adds none. Local Criterion means (Windows x86_64, Rust stable, cargo bench):

  • Refined::new with Ascii on a short &str: ~0.9 ns
  • Refined::new with LenRange<3, 16> on a &str: ~2.2 ns
  • Refined::new with InRange<0, 100> on an i32: ~2.6 ns
  • get / Deref accessors: sub-nanosecond (compile down to a field read)

API Overview

For the complete reference with examples, see docs/API.md.

Runnable demos live in examples/: quick_start, built_in_rules, composing_rules, custom_rule, and derive_newtype (e.g. cargo run --example quick_start).


Installation

[dependencies]
type-lib = "1.0.0"

# with the derive macro
type-lib = { version = "1.0.0", features = ["derive"] }

# no_std build (core API + borrowed-value rules)
type-lib = { version = "1.0.0", default-features = false }

# no_std + owned-type rules (String / Vec)
type-lib = { version = "1.0.0", default-features = false, features = ["alloc"] }

MSRV: Rust 1.75.

Quick start

use type_lib::combinator::And;
use type_lib::rules::{LenRange, Trimmed};
use type_lib::Refined;

// A username: 3–16 characters with no surrounding whitespace.
// Built once, the type guarantees the invariant everywhere it is used.
type Username = Refined<String, And<Trimmed, LenRange<3, 16>>>;

fn main() {
    let user = Username::new("alice".to_owned());
    assert!(user.is_ok());

    assert!(Username::new("ab".to_owned()).is_err());        // too short
    assert!(Username::new("  alice  ".to_owned()).is_err()); // whitespace
}

Prefer a distinct named type with its own constructor? Enable the derive feature and annotate a newtype:

use type_lib::combinator::And;
use type_lib::rules::{LenRange, Trimmed};
use type_lib::Validated;

#[derive(Validated)]
#[valid(And<Trimmed, LenRange<3, 16>>)]
pub struct Username(String);

let user = Username::new("alice".to_owned());
assert!(user.is_ok());

Need a rule the built-ins don't cover? Implement Validator on a marker type — see the custom_rule example.

For the exhaustive API reference, see docs/API.md.


Standards

  • REPS governs every decision. See REPS.md.
  • MSRV: Rust 1.75.
  • Edition: 2021.
  • Cross-platform: Linux, macOS, Windows.

License

Dual-licensed under either of:

at your option.