use facet::Facet;
use facet_assert::{SameReport, assert_same, assert_sameish, check_same_report};
use facet_showcase::{Language, OutputMode, ShowcaseRunner, ansi_to_html};
use owo_colors::OwoColorize;
#[derive(Facet)]
struct Config {
host: String,
port: u16,
debug: bool,
tags: Vec<String>,
}
#[derive(Facet)]
struct ConfigV2 {
host: String,
port: u16,
debug: bool,
tags: Vec<String>,
}
#[derive(Facet)]
struct Person {
name: String,
age: u32,
address: Address,
}
#[derive(Facet)]
struct Address {
street: String,
city: String,
}
fn main() {
let mut runner = ShowcaseRunner::new("Assertions").language(Language::Rust);
let mode = runner.mode();
runner.header();
runner.intro("[`facet-assert`](https://docs.rs/facet-assert) provides structural assertions for any `Facet` type without requiring `PartialEq` or `Debug`. Compare values across different types with identical structure, and get precise structural diffs showing exactly which fields differ.");
scenario_same_values(&mut runner, mode);
scenario_cross_type(&mut runner, mode);
scenario_nested(&mut runner, mode);
scenario_diff_output(&mut runner, mode);
scenario_vector_diff(&mut runner, mode);
runner.footer();
}
fn scenario_same_values(runner: &mut ShowcaseRunner, _mode: OutputMode) {
let config1 = Config {
host: "localhost".into(),
port: 8080,
debug: true,
tags: vec!["prod".into(), "api".into()],
};
let config2 = Config {
host: "localhost".into(),
port: 8080,
debug: true,
tags: vec!["prod".into(), "api".into()],
};
assert_same!(config1, config2);
runner
.scenario("Same Values")
.description(
"Two values with identical content pass `assert_same!` — no `PartialEq` required.",
)
.target_type::<Config>()
.success(&config1)
.finish();
}
fn scenario_cross_type(runner: &mut ShowcaseRunner, _mode: OutputMode) {
let v1 = Config {
host: "localhost".into(),
port: 8080,
debug: true,
tags: vec!["prod".into()],
};
let v2 = ConfigV2 {
host: "localhost".into(),
port: 8080,
debug: true,
tags: vec!["prod".into()],
};
assert_sameish!(v1, v2);
let mut scenario = runner.scenario("Cross-Type Comparison");
scenario = scenario.description(
"Different type names (`Config` vs `ConfigV2`) with the same structure are considered \"same\". \
Useful for comparing DTOs across API versions or testing serialization roundtrips.",
);
scenario = scenario.target_type::<Config>();
scenario = scenario.success(&v1);
scenario.finish();
}
fn scenario_nested(runner: &mut ShowcaseRunner, _mode: OutputMode) {
let person1 = Person {
name: "Alice".into(),
age: 30,
address: Address {
street: "123 Main St".into(),
city: "Springfield".into(),
},
};
let person2 = Person {
name: "Alice".into(),
age: 30,
address: Address {
street: "123 Main St".into(),
city: "Springfield".into(),
},
};
assert_same!(person1, person2);
runner
.scenario("Nested Structs")
.description("Nested structs are compared recursively, field by field.")
.target_type::<Person>()
.success(&person1)
.finish();
}
fn scenario_diff_output(runner: &mut ShowcaseRunner, mode: OutputMode) {
let config_a = Config {
host: "localhost".into(),
port: 8080,
debug: true,
tags: vec!["prod".into(), "api".into()],
};
let config_b = Config {
host: "prod.example.com".into(),
port: 443,
debug: false,
tags: vec!["prod".into()],
};
let report = match check_same_report(&config_a, &config_b) {
SameReport::Different(report) => report,
_ => unreachable!(),
};
let rust_diff = report.legacy_string();
let json_diff = report.render_ansi_json();
let xml_diff = report.render_ansi_xml();
let outputs = [
("Rust Diff Output", rust_diff.as_str()),
("JSON Diff Output", json_diff.as_str()),
("XML Diff Output", xml_diff.as_str()),
];
print_diff_scenario(
runner,
mode,
"Structural Diff",
"When values differ, you get a precise structural diff showing exactly which fields changed \
and at what path — then render it as Rust, JSON, or XML for whichever toolchain you need.",
&outputs,
);
}
fn scenario_vector_diff(runner: &mut ShowcaseRunner, mode: OutputMode) {
let a = vec![1, 2, 3, 4, 5];
let b = vec![1, 2, 99, 4];
let report = match check_same_report(&a, &b) {
SameReport::Different(report) => report,
_ => unreachable!(),
};
let diff = report.legacy_string();
let outputs = [("Diff Output", diff.as_str())];
print_diff_scenario(
runner,
mode,
"Vector Differences",
"Vector comparisons show exactly which indices differ, which elements were added, \
and which were removed.",
&outputs,
);
}
fn print_diff_scenario(
_runner: &mut ShowcaseRunner,
mode: OutputMode,
name: &str,
description: &str,
outputs: &[(&str, &str)],
) {
match mode {
OutputMode::Terminal => {
println!();
println!("{}", "═".repeat(78).dimmed());
println!("{} {}", "SCENARIO:".bold().cyan(), name.bold().white());
println!("{}", "─".repeat(78).dimmed());
println!("{}", description.dimmed());
println!("{}", "═".repeat(78).dimmed());
for (label, diff) in outputs {
println!();
println!("{}", format!("{label}:").bold().yellow());
println!("{}", "─".repeat(60).dimmed());
print!("{diff}");
println!("{}", "─".repeat(60).dimmed());
}
}
OutputMode::Markdown => {
println!();
println!("## {name}");
println!();
println!("<section class=\"scenario\">");
println!("<p class=\"description\">{description}</p>");
for (label, diff) in outputs {
println!("<div class=\"diff-output\">");
println!("<h4>{label}</h4>");
println!(
"<div class=\"code-block\"><pre><code>{}</code></pre></div>",
ansi_to_html(diff)
);
println!("</div>");
}
println!("</section>");
}
}
}