THE DIFFERENCE
One messy payload. Two outcomes.
Same input. serde stops at the first surprise; laminate keeps going and tells you what it did.
With serde alone
Port arrives as the string "8080". One bad field, nothing parsed.
let cfg: Config = serde_json::from_str(body)?;
// Error: invalid type: string "8080",
// expected u16 at line 1 column 15
// (nothing else parsed)
With laminate
Coerce on read, keep going, report what happened. Your code runs; your logs tell the story.
let shaped = Config::shape_lenient(body)?;
// shaped.value -> Config { port: 8080, .. }
// shaped.diagnostics
// [Info] coerced port: String -> u16
// [Info] coerced debug: "yes" -> true
| Feature | laminate | serde_with | eserde | figment | config |
|---|---|---|---|---|---|
| Type coercion | 4 levels, graduated | Per-field macros | No | No | Basic |
| Multi-error | Yes (partial success) | No | Yes (all-Err) | No | No |
| Type detection | 16+ types, confidence | No | No | No | No |
| Source hints | CSV/JSON/Env | No | No | Provider merging | Provider merging |
| Diagnostics | Risk-leveled | No | Error list | No | No |
| Domain packs | 6 domains | No | No | No | No |
| AI/LLM adapters | Anthropic/OpenAI/Ollama | No | No | No | No |
| Schema inference | Yes | No | No | No | No |
| Derive macro | Yes (7 attributes) | Yes (macros) | Yes (derive) | No | No |
| Streaming/SSE | Yes | No | No | No | No |
When to use Laminate
- You're consuming external data (APIs, CSV, config) and need forgiving parsing
- You want to know WHAT was coerced, not just that it worked
- You need type detection on unknown data
- You're building AI/LLM applications with multi-provider support
- You need domain-specific parsing (medical, financial, logistics)
When NOT to use Laminate
- You control both producer and consumer — use serde directly
- You need maximum deserialization performance with zero overhead — use serde
- You only need per-field customization — serde_with is simpler
- You only need configuration merging — figment or config is more focused