1#![warn(missing_docs)]
2#![forbid(unsafe_code)]
3#![doc = include_str!("../README.md")]
4
5mod same;
11
12pub use facet_diff::DiffReport;
13pub use facet_diff_core::layout::{
14 AnsiBackend, BuildOptions, ColorBackend, DiffFlavor, JsonFlavor, PlainBackend, RenderOptions,
15 RustFlavor, XmlFlavor,
16};
17pub use same::{
18 SameOptions, SameReport, Sameness, check_same, check_same_report, check_same_with,
19 check_same_with_report, check_sameish, check_sameish_report, check_sameish_with,
20 check_sameish_with_report,
21};
22
23#[macro_export]
68macro_rules! assert_same {
69 ($left:expr, $right:expr $(,)?) => {
70 match $crate::check_same(&$left, &$right) {
71 $crate::Sameness::Same => {}
72 $crate::Sameness::Different(diff) => {
73 panic!(
74 "assertion `assert_same!(left, right)` failed\n\n{diff}\n"
75 );
76 }
77 $crate::Sameness::Opaque { type_name } => {
78 panic!(
79 "assertion `assert_same!(left, right)` failed: cannot compare opaque type `{type_name}`"
80 );
81 }
82 }
83 };
84 ($left:expr, $right:expr, $($arg:tt)+) => {
85 match $crate::check_same(&$left, &$right) {
86 $crate::Sameness::Same => {}
87 $crate::Sameness::Different(diff) => {
88 panic!(
89 "assertion `assert_same!(left, right)` failed: {}\n\n{diff}\n",
90 format_args!($($arg)+)
91 );
92 }
93 $crate::Sameness::Opaque { type_name } => {
94 panic!(
95 "assertion `assert_same!(left, right)` failed: {}: cannot compare opaque type `{type_name}`",
96 format_args!($($arg)+)
97 );
98 }
99 }
100 };
101}
102
103#[macro_export]
126macro_rules! assert_same_with {
127 ($left:expr, $right:expr, $options:expr $(,)?) => {
128 match $crate::check_same_with(&$left, &$right, $options) {
129 $crate::Sameness::Same => {}
130 $crate::Sameness::Different(diff) => {
131 panic!(
132 "assertion `assert_same_with!(left, right, options)` failed\n\n{diff}\n"
133 );
134 }
135 $crate::Sameness::Opaque { type_name } => {
136 panic!(
137 "assertion `assert_same_with!(left, right, options)` failed: cannot compare opaque type `{type_name}`"
138 );
139 }
140 }
141 };
142 ($left:expr, $right:expr, $options:expr, $($arg:tt)+) => {
143 match $crate::check_same_with(&$left, &$right, $options) {
144 $crate::Sameness::Same => {}
145 $crate::Sameness::Different(diff) => {
146 panic!(
147 "assertion `assert_same_with!(left, right, options)` failed: {}\n\n{diff}\n",
148 format_args!($($arg)+)
149 );
150 }
151 $crate::Sameness::Opaque { type_name } => {
152 panic!(
153 "assertion `assert_same_with!(left, right, options)` failed: {}: cannot compare opaque type `{type_name}`",
154 format_args!($($arg)+)
155 );
156 }
157 }
158 };
159}
160
161#[macro_export]
165macro_rules! debug_assert_same {
166 ($($arg:tt)*) => {
167 if cfg!(debug_assertions) {
168 $crate::assert_same!($($arg)*);
169 }
170 };
171}
172
173#[macro_export]
177macro_rules! debug_assert_same_with {
178 ($($arg:tt)*) => {
179 if cfg!(debug_assertions) {
180 $crate::assert_same_with!($($arg)*);
181 }
182 };
183}
184
185#[macro_export]
226macro_rules! assert_sameish {
227 ($left:expr, $right:expr $(,)?) => {
228 match $crate::check_sameish(&$left, &$right) {
229 $crate::Sameness::Same => {}
230 $crate::Sameness::Different(diff) => {
231 panic!(
232 "assertion `assert_sameish!(left, right)` failed\n\n{diff}\n"
233 );
234 }
235 $crate::Sameness::Opaque { type_name } => {
236 panic!(
237 "assertion `assert_sameish!(left, right)` failed: cannot compare opaque type `{type_name}`"
238 );
239 }
240 }
241 };
242 ($left:expr, $right:expr, $($arg:tt)+) => {
243 match $crate::check_sameish(&$left, &$right) {
244 $crate::Sameness::Same => {}
245 $crate::Sameness::Different(diff) => {
246 panic!(
247 "assertion `assert_sameish!(left, right)` failed: {}\n\n{diff}\n",
248 format_args!($($arg)+)
249 );
250 }
251 $crate::Sameness::Opaque { type_name } => {
252 panic!(
253 "assertion `assert_sameish!(left, right)` failed: {}: cannot compare opaque type `{type_name}`",
254 format_args!($($arg)+)
255 );
256 }
257 }
258 };
259}
260
261#[macro_export]
265macro_rules! assert_sameish_with {
266 ($left:expr, $right:expr, $options:expr $(,)?) => {
267 match $crate::check_sameish_with(&$left, &$right, $options) {
268 $crate::Sameness::Same => {}
269 $crate::Sameness::Different(diff) => {
270 panic!(
271 "assertion `assert_sameish_with!(left, right, options)` failed\n\n{diff}\n"
272 );
273 }
274 $crate::Sameness::Opaque { type_name } => {
275 panic!(
276 "assertion `assert_sameish_with!(left, right, options)` failed: cannot compare opaque type `{type_name}`"
277 );
278 }
279 }
280 };
281 ($left:expr, $right:expr, $options:expr, $($arg:tt)+) => {
282 match $crate::check_sameish_with(&$left, &$right, $options) {
283 $crate::Sameness::Same => {}
284 $crate::Sameness::Different(diff) => {
285 panic!(
286 "assertion `assert_sameish_with!(left, right, options)` failed: {}\n\n{diff}\n",
287 format_args!($($arg)+)
288 );
289 }
290 $crate::Sameness::Opaque { type_name } => {
291 panic!(
292 "assertion `assert_sameish_with!(left, right, options)` failed: {}: cannot compare opaque type `{type_name}`",
293 format_args!($($arg)+)
294 );
295 }
296 }
297 };
298}
299
300#[macro_export]
304macro_rules! debug_assert_sameish {
305 ($($arg:tt)*) => {
306 if cfg!(debug_assertions) {
307 $crate::assert_sameish!($($arg)*);
308 }
309 };
310}
311
312#[macro_export]
316macro_rules! debug_assert_sameish_with {
317 ($($arg:tt)*) => {
318 if cfg!(debug_assertions) {
319 $crate::assert_sameish_with!($($arg)*);
320 }
321 };
322}
323
324#[cfg(test)]
325mod tests {
326 use super::*;
327 use facet::Facet;
328
329 #[derive(Facet)]
330 struct Person {
331 name: String,
332 age: u32,
333 }
334
335 #[derive(Facet)]
336 struct PersonV2 {
337 name: String,
338 age: u32,
339 }
340
341 #[derive(Facet)]
342 struct Different {
343 name: String,
344 score: f64,
345 }
346
347 #[test]
348 fn same_type_same_values() {
349 let a = Person {
350 name: "Alice".into(),
351 age: 30,
352 };
353 let b = Person {
354 name: "Alice".into(),
355 age: 30,
356 };
357 assert_same!(a, b);
358 }
359
360 #[test]
361 fn different_types_same_structure() {
362 let a = Person {
363 name: "Alice".into(),
364 age: 30,
365 };
366 let b = PersonV2 {
367 name: "Alice".into(),
368 age: 30,
369 };
370 assert_sameish!(a, b);
372 }
373
374 #[test]
375 fn same_type_different_values() {
376 let a = Person {
377 name: "Alice".into(),
378 age: 30,
379 };
380 let b = Person {
381 name: "Bob".into(),
382 age: 30,
383 };
384
385 match check_same(&a, &b) {
386 Sameness::Different(_) => {} other => panic!(
388 "expected Different, got {:?}",
389 matches!(other, Sameness::Same)
390 ),
391 }
392 }
393
394 #[test]
395 fn diff_report_renders_multiple_flavors() {
396 let a = Person {
397 name: "Alice".into(),
398 age: 30,
399 };
400 let b = Person {
401 name: "Bob".into(),
402 age: 45,
403 };
404
405 let report = match check_same_report(&a, &b) {
406 SameReport::Different(report) => report,
407 _ => panic!("expected Different"),
408 };
409
410 let rust = report.render_plain_rust();
411 assert!(rust.contains("Person"));
412
413 let json = report.render_plain_json();
414 assert!(json.contains("\"name\""));
415
416 let xml = report.render_plain_xml();
417 assert!(xml.contains("<@Person"));
419 }
420
421 #[test]
422 fn primitives() {
423 assert_same!(42i32, 42i32);
424 assert_same!("hello", "hello");
425 assert_same!(true, true);
426 }
427
428 #[test]
429 fn vectors() {
430 let a = vec![1, 2, 3];
431 let b = vec![1, 2, 3];
432 assert_same!(a, b);
433 }
434
435 #[test]
436 fn vectors_different() {
437 let a = vec![1, 2, 3];
438 let b = vec![1, 2, 4];
439
440 match check_same(&a, &b) {
441 Sameness::Different(_) => {} _ => panic!("expected Different"),
443 }
444 }
445
446 #[test]
447 fn options() {
448 let a: Option<i32> = Some(42);
449 let b: Option<i32> = Some(42);
450 assert_same!(a, b);
451
452 let c: Option<i32> = None;
453 let d: Option<i32> = None;
454 assert_same!(c, d);
455 }
456
457 #[test]
458 fn float_exact_same() {
459 let a = 1.0_f64;
460 let b = 1.0_f64;
461 assert_same!(a, b);
462 }
463
464 #[test]
465 fn float_exact_different() {
466 let a = 1.0000001_f64;
467 let b = 1.0000002_f64;
468
469 match check_same(&a, &b) {
470 Sameness::Different(_) => {} _ => panic!("expected Different"),
472 }
473 }
474
475 #[test]
476 fn float_with_tolerance_same() {
477 let a = 1.0000001_f64;
478 let b = 1.0000002_f64;
479
480 assert_same_with!(a, b, SameOptions::new().float_tolerance(1e-6));
482 }
483
484 #[test]
485 fn float_with_tolerance_different() {
486 let a = 1.0_f64;
487 let b = 2.0_f64;
488
489 match check_same_with(&a, &b, SameOptions::new().float_tolerance(1e-6)) {
491 Sameness::Different(_) => {} _ => panic!("expected Different"),
493 }
494 }
495
496 #[test]
497 fn f32_with_tolerance() {
498 let a = 1.0000001_f32;
499 let b = 1.0000002_f32;
500
501 assert_same_with!(a, b, SameOptions::new().float_tolerance(1e-5));
503 }
504
505 #[test]
506 fn struct_with_float_tolerance() {
507 #[derive(Facet)]
508 struct Measurement {
509 name: String,
510 value: f64,
511 }
512
513 let a = Measurement {
514 name: "temperature".into(),
515 value: 98.6000001,
516 };
517 let b = Measurement {
518 name: "temperature".into(),
519 value: 98.6000002,
520 };
521
522 match check_same(&a, &b) {
524 Sameness::Different(_) => {} _ => panic!("expected Different"),
526 }
527
528 assert_same_with!(a, b, SameOptions::new().float_tolerance(1e-6));
530 }
531
532 #[test]
533 fn vec_of_floats_with_tolerance() {
534 let a = vec![1.0000001_f64, 2.0000001_f64, 3.0000001_f64];
535 let b = vec![1.0000002_f64, 2.0000002_f64, 3.0000002_f64];
536
537 match check_same(&a, &b) {
539 Sameness::Different(_) => {} _ => panic!("expected Different"),
541 }
542
543 assert_same_with!(a, b, SameOptions::new().float_tolerance(1e-6));
545 }
546
547 #[test]
548 fn nested_struct_with_float_tolerance() {
549 #[derive(Facet)]
550 struct Point {
551 x: f64,
552 y: f64,
553 }
554
555 #[derive(Facet)]
556 struct Line {
557 start: Point,
558 end: Point,
559 }
560
561 let a = Line {
562 start: Point {
563 x: 0.0000001,
564 y: 0.0000001,
565 },
566 end: Point {
567 x: 1.0000001,
568 y: 1.0000001,
569 },
570 };
571 let b = Line {
572 start: Point {
573 x: 0.0000002,
574 y: 0.0000002,
575 },
576 end: Point {
577 x: 1.0000002,
578 y: 1.0000002,
579 },
580 };
581
582 assert_same_with!(a, b, SameOptions::new().float_tolerance(1e-6));
583 }
584
585 mod type_inference {
587 use super::*;
588
589 #[test]
590 fn with_option() {
591 let x: Option<Option<i32>> = Some(None);
594 assert_same!(x, Some(None)); }
596
597 #[test]
598 fn with_nested_option() {
599 let x: Option<Option<Option<i32>>> = Some(Some(None));
600 assert_same!(x, Some(Some(None))); }
602
603 #[test]
604 fn with_result() {
605 let x: Result<Option<i32>, ()> = Ok(None);
606 assert_same!(x, Ok(None)); }
608
609 #[test]
610 fn with_vec() {
611 let x: Vec<Option<i32>> = vec![Some(1), None, Some(3)];
612 assert_same!(x, vec![Some(1), None, Some(3)]);
613 }
614 }
615
616 mod sameish {
618 use super::*;
619
620 #[test]
621 fn different_types_same_structure() {
622 let a = Person {
623 name: "Alice".into(),
624 age: 30,
625 };
626 let b = PersonV2 {
627 name: "Alice".into(),
628 age: 30,
629 };
630 assert_sameish!(a, b);
631 }
632
633 #[test]
634 fn check_sameish_detects_differences() {
635 let a = Person {
636 name: "Alice".into(),
637 age: 30,
638 };
639 let b = PersonV2 {
640 name: "Bob".into(),
641 age: 30,
642 };
643
644 match check_sameish(&a, &b) {
645 Sameness::Different(_) => {} _ => panic!("expected Different"),
647 }
648 }
649
650 #[test]
651 fn with_options_float_tolerance() {
652 #[derive(Facet)]
653 struct MeasurementV1 {
654 value: f64,
655 }
656
657 #[derive(Facet)]
658 struct MeasurementV2 {
659 value: f64,
660 }
661
662 let a = MeasurementV1 { value: 1.0000001 };
663 let b = MeasurementV2 { value: 1.0000002 };
664
665 assert_sameish_with!(a, b, SameOptions::new().float_tolerance(1e-6));
666 }
667
668 #[test]
669 fn with_custom_message() {
670 let a = Person {
671 name: "Alice".into(),
672 age: 30,
673 };
674 let b = PersonV2 {
675 name: "Alice".into(),
676 age: 30,
677 };
678 assert_sameish!(a, b, "custom message: {} vs {}", "Person", "PersonV2");
679 }
680 }
681}