facet-assert
Pretty assertions for Facet types.
What makes this different?
No PartialEq required
Standard Rust assertions need PartialEq:
assert_eq!(a, b); // Requires: PartialEq + Debug
facet-assert uses structural comparison via reflection:
assert_same!(a, b); // Requires: Facet (that's it!)
This works because Facet gives us full introspection into any type's structure.
Type inference works naturally
Unlike some reflection-based comparison macros, assert_same! supports full
type inference:
let x: Option<Option<i32>> = Some(None);
assert_same!(x, Some(None)); // Type of Some(None) inferred from x
Cross-type comparison with assert_sameish!
Need to compare values of different types? Use assert_sameish!:
#[derive(Facet)]
struct PersonV1 { name: String, age: u32 }
#[derive(Facet)]
struct PersonV2 { name: String, age: u32 }
let a = PersonV1 { name: "Alice".into(), age: 30 };
let b = PersonV2 { name: "Alice".into(), age: 30 };
assert_sameish!(a, b); // Passes! Same structure, same values.
This is useful for:
- Comparing DTOs across API versions
- Testing serialization roundtrips (JSON → struct → JSON)
- Comparing values parsed from different formats (YAML vs TOML vs JSON)
Smart structural diffs
When values differ, you get a structural diff — not just line-by-line text comparison. We know which fields changed:
.host:
- localhost
+ prod.example.com
.port:
- 8080
+ 443
.tags[1] (only in left):
- api
Instead of a wall of red/green like traditional diff tools.
Render diffs in your format
Want the diff in JSON or XML so another tool can consume it? Call
check_same_report to get a SameReport. When values differ you receive a
DiffReport that can render the change set in Rust, JSON, or XML layouts with
or without ANSI colors.
use facet_assert::{SameReport, check_same_report};
let report = match check_same_report(&c_output, &rust_output) {
SameReport::Different(report) => report,
SameReport::Same => return,
SameReport::Opaque { type_name } => panic!("opaque type {type_name}"),
};
let rust_view = report.legacy_string();
let json_view = report.render_plain_json();
let xml_view = report.render_plain_xml();
For full control, use render_with_options and pass your own BuildOptions,
RenderOptions, or even a custom DiffFlavor implementation.
Opaque types fail clearly
If a type cannot be inspected (opaque), the assertion fails with a clear message rather than silently giving wrong results:
assertion `assert_same!(left, right)` failed: cannot compare opaque type `SomeOpaqueType`
Usage
use facet::Facet;
use facet_assert::assert_same;
#[derive(Facet)]
struct Config {
host: String,
port: u16,
debug: bool,
}
#[test]
fn test_config_parsing() {
let from_json: Config = parse_json("...");
let from_yaml: Config = parse_yaml("...");
assert_same!(from_json, from_yaml);
}
Macros
Same-type comparison (the common case)
assert_same!(a, b)— panics ifaandbare not structurally sameassert_same!(a, b, "message {}", x)— with custom messageassert_same_with!(a, b, options)— with custom comparison optionsdebug_assert_same!(...)— only in debug builds
Cross-type comparison (for migrations, etc.)
assert_sameish!(a, b)— compare values of different typesassert_sameish_with!(a, b, options)— with custom comparison optionsdebug_assert_sameish!(...)— only in debug builds
Sponsors
Thanks to all individual sponsors:
...along with corporate sponsors:
...without whom this work could not exist.
Special thanks
The facet logo was drawn by Misiasart.
License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.