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,
20};
21
22#[macro_export]
52macro_rules! assert_same {
53 ($left:expr, $right:expr $(,)?) => {
54 match $crate::check_same(&$left, &$right) {
55 $crate::Sameness::Same => {}
56 $crate::Sameness::Different(diff) => {
57 panic!(
58 "assertion `assert_same!(left, right)` failed\n\n{diff}\n"
59 );
60 }
61 $crate::Sameness::Opaque { type_name } => {
62 panic!(
63 "assertion `assert_same!(left, right)` failed: cannot compare opaque type `{type_name}`"
64 );
65 }
66 }
67 };
68 ($left:expr, $right:expr, $($arg:tt)+) => {
69 match $crate::check_same(&$left, &$right) {
70 $crate::Sameness::Same => {}
71 $crate::Sameness::Different(diff) => {
72 panic!(
73 "assertion `assert_same!(left, right)` failed: {}\n\n{diff}\n",
74 format_args!($($arg)+)
75 );
76 }
77 $crate::Sameness::Opaque { type_name } => {
78 panic!(
79 "assertion `assert_same!(left, right)` failed: {}: cannot compare opaque type `{type_name}`",
80 format_args!($($arg)+)
81 );
82 }
83 }
84 };
85}
86
87#[macro_export]
113macro_rules! assert_same_with {
114 ($left:expr, $right:expr, $options:expr $(,)?) => {
115 match $crate::check_same_with(&$left, &$right, $options) {
116 $crate::Sameness::Same => {}
117 $crate::Sameness::Different(diff) => {
118 panic!(
119 "assertion `assert_same_with!(left, right, options)` failed\n\n{diff}\n"
120 );
121 }
122 $crate::Sameness::Opaque { type_name } => {
123 panic!(
124 "assertion `assert_same_with!(left, right, options)` failed: cannot compare opaque type `{type_name}`"
125 );
126 }
127 }
128 };
129 ($left:expr, $right:expr, $options:expr, $($arg:tt)+) => {
130 match $crate::check_same_with(&$left, &$right, $options) {
131 $crate::Sameness::Same => {}
132 $crate::Sameness::Different(diff) => {
133 panic!(
134 "assertion `assert_same_with!(left, right, options)` failed: {}\n\n{diff}\n",
135 format_args!($($arg)+)
136 );
137 }
138 $crate::Sameness::Opaque { type_name } => {
139 panic!(
140 "assertion `assert_same_with!(left, right, options)` failed: {}: cannot compare opaque type `{type_name}`",
141 format_args!($($arg)+)
142 );
143 }
144 }
145 };
146}
147
148#[macro_export]
152macro_rules! debug_assert_same {
153 ($($arg:tt)*) => {
154 if cfg!(debug_assertions) {
155 $crate::assert_same!($($arg)*);
156 }
157 };
158}
159
160#[macro_export]
164macro_rules! debug_assert_same_with {
165 ($($arg:tt)*) => {
166 if cfg!(debug_assertions) {
167 $crate::assert_same_with!($($arg)*);
168 }
169 };
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175 use facet::Facet;
176
177 #[derive(Facet)]
178 struct Person {
179 name: String,
180 age: u32,
181 }
182
183 #[derive(Facet)]
184 struct PersonV2 {
185 name: String,
186 age: u32,
187 }
188
189 #[derive(Facet)]
190 struct Different {
191 name: String,
192 score: f64,
193 }
194
195 #[test]
196 fn same_type_same_values() {
197 let a = Person {
198 name: "Alice".into(),
199 age: 30,
200 };
201 let b = Person {
202 name: "Alice".into(),
203 age: 30,
204 };
205 assert_same!(a, b);
206 }
207
208 #[test]
209 fn different_types_same_structure() {
210 let a = Person {
211 name: "Alice".into(),
212 age: 30,
213 };
214 let b = PersonV2 {
215 name: "Alice".into(),
216 age: 30,
217 };
218 assert_same!(a, b);
219 }
220
221 #[test]
222 fn same_type_different_values() {
223 let a = Person {
224 name: "Alice".into(),
225 age: 30,
226 };
227 let b = Person {
228 name: "Bob".into(),
229 age: 30,
230 };
231
232 match check_same(&a, &b) {
233 Sameness::Different(_) => {} other => panic!(
235 "expected Different, got {:?}",
236 matches!(other, Sameness::Same)
237 ),
238 }
239 }
240
241 #[test]
242 fn diff_report_renders_multiple_flavors() {
243 let a = Person {
244 name: "Alice".into(),
245 age: 30,
246 };
247 let b = Person {
248 name: "Bob".into(),
249 age: 45,
250 };
251
252 let report = match check_same_report(&a, &b) {
253 SameReport::Different(report) => report,
254 _ => panic!("expected Different"),
255 };
256
257 let rust = report.render_plain_rust();
258 assert!(rust.contains("Person"));
259
260 let json = report.render_plain_json();
261 assert!(json.contains("\"name\""));
262
263 let xml = report.render_plain_xml();
264 assert!(xml.contains("<@Person"));
266 }
267
268 #[test]
269 fn primitives() {
270 assert_same!(42i32, 42i32);
271 assert_same!("hello", "hello");
272 assert_same!(true, true);
273 }
274
275 #[test]
276 fn vectors() {
277 let a = vec![1, 2, 3];
278 let b = vec![1, 2, 3];
279 assert_same!(a, b);
280 }
281
282 #[test]
283 fn vectors_different() {
284 let a = vec![1, 2, 3];
285 let b = vec![1, 2, 4];
286
287 match check_same(&a, &b) {
288 Sameness::Different(_) => {} _ => panic!("expected Different"),
290 }
291 }
292
293 #[test]
294 fn options() {
295 let a: Option<i32> = Some(42);
296 let b: Option<i32> = Some(42);
297 assert_same!(a, b);
298
299 let c: Option<i32> = None;
300 let d: Option<i32> = None;
301 assert_same!(c, d);
302 }
303
304 #[test]
305 fn float_exact_same() {
306 let a = 1.0_f64;
307 let b = 1.0_f64;
308 assert_same!(a, b);
309 }
310
311 #[test]
312 fn float_exact_different() {
313 let a = 1.0000001_f64;
314 let b = 1.0000002_f64;
315
316 match check_same(&a, &b) {
317 Sameness::Different(_) => {} _ => panic!("expected Different"),
319 }
320 }
321
322 #[test]
323 fn float_with_tolerance_same() {
324 let a = 1.0000001_f64;
325 let b = 1.0000002_f64;
326
327 assert_same_with!(a, b, SameOptions::new().float_tolerance(1e-6));
329 }
330
331 #[test]
332 fn float_with_tolerance_different() {
333 let a = 1.0_f64;
334 let b = 2.0_f64;
335
336 match check_same_with(&a, &b, SameOptions::new().float_tolerance(1e-6)) {
338 Sameness::Different(_) => {} _ => panic!("expected Different"),
340 }
341 }
342
343 #[test]
344 fn f32_with_tolerance() {
345 let a = 1.0000001_f32;
346 let b = 1.0000002_f32;
347
348 assert_same_with!(a, b, SameOptions::new().float_tolerance(1e-5));
350 }
351
352 #[test]
353 fn struct_with_float_tolerance() {
354 #[derive(Facet)]
355 struct Measurement {
356 name: String,
357 value: f64,
358 }
359
360 let a = Measurement {
361 name: "temperature".into(),
362 value: 98.6000001,
363 };
364 let b = Measurement {
365 name: "temperature".into(),
366 value: 98.6000002,
367 };
368
369 match check_same(&a, &b) {
371 Sameness::Different(_) => {} _ => panic!("expected Different"),
373 }
374
375 assert_same_with!(a, b, SameOptions::new().float_tolerance(1e-6));
377 }
378
379 #[test]
380 fn vec_of_floats_with_tolerance() {
381 let a = vec![1.0000001_f64, 2.0000001_f64, 3.0000001_f64];
382 let b = vec![1.0000002_f64, 2.0000002_f64, 3.0000002_f64];
383
384 match check_same(&a, &b) {
386 Sameness::Different(_) => {} _ => panic!("expected Different"),
388 }
389
390 assert_same_with!(a, b, SameOptions::new().float_tolerance(1e-6));
392 }
393
394 #[test]
395 fn nested_struct_with_float_tolerance() {
396 #[derive(Facet)]
397 struct Point {
398 x: f64,
399 y: f64,
400 }
401
402 #[derive(Facet)]
403 struct Line {
404 start: Point,
405 end: Point,
406 }
407
408 let a = Line {
409 start: Point {
410 x: 0.0000001,
411 y: 0.0000001,
412 },
413 end: Point {
414 x: 1.0000001,
415 y: 1.0000001,
416 },
417 };
418 let b = Line {
419 start: Point {
420 x: 0.0000002,
421 y: 0.0000002,
422 },
423 end: Point {
424 x: 1.0000002,
425 y: 1.0000002,
426 },
427 };
428
429 assert_same_with!(a, b, SameOptions::new().float_tolerance(1e-6));
430 }
431}