Laminate › Features

Everything in the box

Deep dive into every Laminate capability

COERCION ENGINE

Four coercion levels, one API

Level What it does Use when
Exact No type conversion at all Production validation
SafeWidening int→float, bool→int (lossless only) Conservative pipelines
StringCoercion "42"→42, "true"→true, null sentinels CSV/config/env data
BestEffort Everything above + JSON stringify, array unwrap Exploratory/prototyping

Every coercion produces a diagnostic — what happened, the risk level, and a suggestion for tightening.

OPERATIONAL MODES

Lenient, Absorbing, Strict

A mode sets four switches at once. It's a type parameter, so the choice is visible in the type and checked at compile time — Strict carries a residual of Infallible, making a strict result a compile-time proof that nothing was coerced or left over.

Lenient Absorbing Strict
Unknown fields dropped preserved in overflow error
Coercion level BestEffort SafeWidening Exact
Missing fields defaulted error error
On error collect all collect all fail fast
Residual type () Overflow (HashMap) Infallible

When the mode comes from config rather than code, a DynamicMode enum parses from the strings lenient, absorbing, and strict.

DIAGNOSTICS

Every adjustment, recorded

Each coercion, default, drop, and preservation becomes a Diagnostic { path, kind, risk, suggestion }. Risk levels are ordered Info < Warning < Risky, so gating a production path on “no Risky diagnostics” is a one-liner.

Diagnostics route through a DiagnosticSink: collect into a Vec, print to stderr, forward only at or above a risk threshold, or drop them.

TYPE DETECTION

20 data types from raw strings

guess_type() identifies types with confidence scores. Multiple types can match — the full ranked list is returned.

Category Types detected
Primitives Integer, Float, Boolean, NullSentinel
Dates ISO 8601, US/EU dates, Unix timestamps, HL7, compact, week dates
Finance Currency (19 symbols), Credit Card (Luhn), IBAN (MOD-97)
Identity UUID, Email, Phone, SSN, EIN, NHS, NPI, EU VAT, ISBN
Structure JSON, URL, IP Address, Unit Value
DOMAIN PACKS

Built-in parsers for real-world data

Dates

18 format detection and conversion: ISO 8601, US/EU slash dates, dot-separated European, dash-separated, HL7 v2, Unix seconds/millis, abbreviated months, GEDCOM, year-only, ISO week dates.

Currencies

19 symbols (including A$, C$, HK$, €, £, ¥), 29 currency codes, locale detection (US dot-decimal vs European comma-decimal), accounting negative format.

Medical

36 analytes, 44 unit conversions (glucose, cholesterol, HbA1c, creatinine, etc.), reference range classification, clinical calculators (eGFR, BMI, corrected calcium, anion gap, creatinine clearance), FHIR observation parsing, HL7 datetime, pharma abbreviations.

Identifiers

12 types with format validation and checksums: IBAN (MOD-97), Credit Card (Luhn + BIN detection for Visa/MC/Amex/Discover/JCB/Diners/Maestro), ISBN-10, ISBN-13, US SSN, US EIN, US NPI (Luhn), UK NHS (MOD-11), EU VAT (country-specific), UUID, Email, Phone.

Units

Weight, length, volume, temperature, data, time. Qualified weight (gross/net/tare), pack notation (6x500ml, case of 12), UN/ECE code recognition, unit conversion.

Coordinates

Decimal degrees, DMS (degrees/minutes/seconds), DDM (degrees decimal minutes), ISO 6709. Latitude/longitude validation, order detection.

PROVIDER NORMALIZATION

Anthropic, OpenAI, Ollama — one struct

Parse responses from any provider into a unified NormalizedResponse:

providers.rs
// Same struct regardless of provider
let response = parse_openai_response(&raw_body)?;  // or anthropic / ollama
let text = response.text();
let tools = response.tool_uses();
let usage = response.usage; // input_tokens, output_tokens,
                            // cache_read_tokens, cache_creation_tokens

// Model wrapped its JSON in prose or a code fence? Pull it out first (0.4.1):
let data = FlexValue::from_llm_response(model_text)?;

Round-trip: parse -> emit -> re-parse preserves all data. from_llm_response extracts a JSON payload from markdown-fenced or prose-wrapped model output before parsing, while from_json stays strict. Token usage is normalized across providers, including cache_read_tokens from OpenAI and Anthropic prompt caching.

STREAMING

Push bytes, get events

The streaming parser is push-style: feed it chunks as they arrive, and it returns parse events incrementally. SSE and newline-delimited JSON both ride on top of it.

streaming.rs
use laminate::{FlexStream, StreamConfig};

let mut stream = FlexStream::new(StreamConfig::default());
for chunk in byte_chunks {
    for event in stream.feed(chunk) {   // feed(&[u8]) -> Vec<StreamEvent>
        handle(event);
    }
}
for event in stream.finish() {          // flush anything still buffered
    handle(event);
}
DERIVE MACRO

Struct shaping with compile-time safety

config.rs
#[derive(Laminate)]
struct Config {
    #[laminate(coerce)]          // auto-convert string -> number
    port: u16,
    #[laminate(coerce, default)] // missing -> false, "yes" -> true
    debug: bool,
    #[laminate(rename = "apiKey")]
    api_key: String,
    #[laminate(overflow)]        // unknown fields -> HashMap
    extra: HashMap<String, Value>,
}

Three shaping modes

shape_lenient()

Coerce and continue, diagnostics available

shape_absorbing()

Capture unknown fields in overflow

shape_strict()

Reject any coercion or unknown fields

Seven field attributes in all: coerce, default, overflow, rename, parse_json_string, flatten, and skip. Enums too: the derive also works on string-valued enums, where a separate #[laminate(unknown)] newtype variant captures an unrecognized value with a diagnostic instead of failing the parse.

SIBLING CRATES

Beyond the core library

laminate-sql

Shape rows straight from a SQL source via sqlx. Enable a backend feature: sqlite, postgres, or mysql.

laminate-cli

Profile and validate data from the command line: infer a schema, audit data against it, and inspect values for detected types.

SCHEMA INFERENCE

Infer structure from data, then audit against it

  • Per-field type detection (dominant type, mixed-type flag)
  • Fill rate and null tracking
  • Required field inference
  • External constraints (override inferred types/nullability)
  • Violation reporting (TypeMismatch, UnexpectedNull, MissingRequired, UnknownField, ConstraintViolation)
SOURCE HINTS

Context-aware coercion defaults

Tell laminate where the data came from, and it adjusts coercion defaults:

Hint Default coercion Why
SourceHint::Csv StringCoercion CSV columns are always strings
SourceHint::Json Exact JSON preserves types
SourceHint::Env StringCoercion Env vars are always strings

laminate 0.4.1 · crates.io · docs.rs · GitHub · v0.4.1 release notes