1#![forbid(unsafe_code)]
2
3use arborium::Highlighter;
4use core::fmt::{Debug, Display};
5use std::any::Any;
6use std::panic::{self, AssertUnwindSafe};
7
8use facet::Facet;
9use facet_assert::assert_same;
10use facet_pretty::{FacetPretty, PrettyPrinter};
11use indoc::formatdoc;
12
13pub trait FormatSuite {
23 type Error: Debug + Display;
25
26 fn format_name() -> &'static str;
28
29 fn highlight_language() -> Option<&'static str> {
31 None
32 }
33
34 fn deserialize<T>(input: &[u8]) -> Result<T, Self::Error>
36 where
37 for<'facet> T: Facet<'facet>,
38 T: Debug;
39
40 fn serialize<T>(value: &T) -> Option<Result<Vec<u8>, String>>
50 where
51 for<'facet> T: Facet<'facet>,
52 T: Debug,
53 {
54 let _ = value;
55 None
56 }
57
58 #[cfg(feature = "tokio")]
65 fn deserialize_async<T>(
66 input: &[u8],
67 ) -> impl std::future::Future<Output = Option<Result<T, Self::Error>>>
68 where
69 for<'facet> T: Facet<'facet>,
70 T: Debug,
71 {
72 let _ = input;
73 async { None }
74 }
75
76 fn struct_single_field() -> CaseSpec;
78 fn sequence_numbers() -> CaseSpec;
80 fn sequence_mixed_scalars() -> CaseSpec;
82 fn struct_nested() -> CaseSpec;
84 fn enum_complex() -> CaseSpec;
86
87 fn attr_rename_field() -> CaseSpec;
91 fn attr_rename_all_camel() -> CaseSpec;
93 fn attr_default_field() -> CaseSpec;
95 fn attr_default_struct() -> CaseSpec;
97 fn attr_default_function() -> CaseSpec;
99 fn option_none() -> CaseSpec;
101 fn option_some() -> CaseSpec;
103 fn option_null() -> CaseSpec;
105 fn attr_skip_serializing() -> CaseSpec;
107 fn attr_skip_serializing_if() -> CaseSpec;
109 fn attr_skip() -> CaseSpec;
111
112 fn enum_internally_tagged() -> CaseSpec;
116 fn enum_adjacently_tagged() -> CaseSpec;
118
119 fn struct_flatten() -> CaseSpec;
123 fn transparent_newtype() -> CaseSpec;
125
126 fn flatten_optional_some() -> CaseSpec;
130 fn flatten_optional_none() -> CaseSpec;
132 fn flatten_overlapping_fields_error() -> CaseSpec;
134 fn flatten_multilevel() -> CaseSpec;
136 fn flatten_multiple_enums() -> CaseSpec;
138
139 fn deny_unknown_fields() -> CaseSpec;
143 fn error_type_mismatch_string_to_int() -> CaseSpec;
145 fn error_type_mismatch_object_to_array() -> CaseSpec;
147 fn error_missing_required_field() -> CaseSpec;
149
150 fn attr_alias() -> CaseSpec;
154
155 fn attr_rename_vs_alias_precedence() -> CaseSpec;
159 fn attr_rename_all_kebab() -> CaseSpec;
161 fn attr_rename_all_screaming() -> CaseSpec;
163 fn attr_rename_unicode() -> CaseSpec;
165 fn attr_rename_special_chars() -> CaseSpec;
167
168 fn proxy_container() -> CaseSpec;
172 fn proxy_field_level() -> CaseSpec;
174 fn proxy_validation_error() -> CaseSpec;
176 fn proxy_with_option() -> CaseSpec;
178 fn proxy_with_enum() -> CaseSpec;
180 fn proxy_with_transparent() -> CaseSpec;
182
183 fn opaque_proxy() -> CaseSpec;
185
186 fn opaque_proxy_option() -> CaseSpec;
188
189 fn transparent_multilevel() -> CaseSpec;
193 fn transparent_option() -> CaseSpec;
195 fn transparent_nonzero() -> CaseSpec;
197
198 fn scalar_bool() -> CaseSpec;
202 fn scalar_integers() -> CaseSpec;
204 fn scalar_floats() -> CaseSpec;
206 fn scalar_floats_scientific() -> CaseSpec;
208
209 #[cfg(feature = "net")]
213 fn net_ip_addr_v4() -> CaseSpec;
214 #[cfg(feature = "net")]
216 fn net_ip_addr_v6() -> CaseSpec;
217 #[cfg(feature = "net")]
219 fn net_ipv4_addr() -> CaseSpec;
220 #[cfg(feature = "net")]
222 fn net_ipv6_addr() -> CaseSpec;
223 #[cfg(feature = "net")]
225 fn net_socket_addr_v4() -> CaseSpec;
226 #[cfg(feature = "net")]
228 fn net_socket_addr_v6() -> CaseSpec;
229 #[cfg(feature = "net")]
231 fn net_socket_addr_v4_explicit() -> CaseSpec;
232 #[cfg(feature = "net")]
234 fn net_socket_addr_v6_explicit() -> CaseSpec;
235
236 fn map_string_keys() -> CaseSpec;
240 fn tuple_simple() -> CaseSpec;
242 fn tuple_nested() -> CaseSpec;
244 fn tuple_empty() -> CaseSpec;
246 fn tuple_single_element() -> CaseSpec;
248 fn tuple_struct_variant() -> CaseSpec;
250 fn tuple_newtype_variant() -> CaseSpec;
252
253 fn enum_unit_variant() -> CaseSpec;
257 fn numeric_enum() -> CaseSpec;
259 fn signed_numeric_enum() -> CaseSpec;
261 fn inferred_numeric_enum() -> CaseSpec;
263 fn enum_untagged() -> CaseSpec;
265 fn enum_variant_rename() -> CaseSpec;
267
268 fn untagged_with_null() -> CaseSpec;
272 fn untagged_newtype_variant() -> CaseSpec;
274 fn untagged_as_field() -> CaseSpec;
276
277 fn untagged_unit_only() -> CaseSpec;
279
280 fn box_wrapper() -> CaseSpec;
284 fn arc_wrapper() -> CaseSpec;
286 fn rc_wrapper() -> CaseSpec;
288 fn box_str() -> CaseSpec;
290 fn arc_str() -> CaseSpec;
292 fn rc_str() -> CaseSpec;
294 fn arc_slice() -> CaseSpec;
296
297 #[cfg(feature = "yoke")]
301 fn yoke_cow_str() -> CaseSpec;
302
303 #[cfg(feature = "yoke")]
305 fn yoke_custom() -> CaseSpec;
306
307 fn set_btree() -> CaseSpec;
311
312 fn scalar_integers_16() -> CaseSpec;
316 fn scalar_integers_128() -> CaseSpec;
318 fn scalar_integers_size() -> CaseSpec;
320
321 fn nonzero_integers() -> CaseSpec;
325 fn nonzero_integers_extended() -> CaseSpec;
327
328 fn cow_str() -> CaseSpec;
332
333 fn newtype_u64() -> CaseSpec;
337 fn newtype_string() -> CaseSpec;
339
340 fn char_scalar() -> CaseSpec;
344
345 fn hashset() -> CaseSpec;
349
350 fn vec_nested() -> CaseSpec;
354
355 fn bytes_vec_u8() -> CaseSpec;
359
360 fn array_fixed_size() -> CaseSpec;
364
365 fn skip_unknown_fields() -> CaseSpec;
369
370 fn string_escapes() -> CaseSpec;
374 fn string_escapes_extended() -> CaseSpec;
376
377 fn unit_struct() -> CaseSpec;
381
382 #[cfg(feature = "uuid")]
386 fn uuid() -> CaseSpec;
387
388 #[cfg(feature = "ulid")]
390 fn ulid() -> CaseSpec;
391
392 #[cfg(feature = "camino")]
394 fn camino_path() -> CaseSpec;
395
396 #[cfg(feature = "ordered-float")]
398 fn ordered_float() -> CaseSpec;
399
400 #[cfg(feature = "rust_decimal")]
402 fn rust_decimal() -> CaseSpec;
403
404 #[cfg(feature = "time")]
406 fn time_offset_datetime() -> CaseSpec;
407
408 #[cfg(feature = "jiff02")]
410 fn jiff_timestamp() -> CaseSpec;
411
412 #[cfg(feature = "jiff02")]
414 fn jiff_civil_datetime() -> CaseSpec;
415
416 #[cfg(feature = "chrono")]
418 fn chrono_datetime_utc() -> CaseSpec;
419
420 #[cfg(feature = "chrono")]
422 fn chrono_naive_datetime() -> CaseSpec;
423
424 #[cfg(feature = "chrono")]
426 fn chrono_naive_date() -> CaseSpec;
427
428 #[cfg(feature = "chrono")]
430 fn chrono_naive_time() -> CaseSpec;
431
432 #[cfg(feature = "chrono")]
434 fn chrono_in_vec() -> CaseSpec;
435
436 #[cfg(feature = "chrono")]
438 fn chrono_duration() -> CaseSpec;
439
440 #[cfg(feature = "chrono")]
442 fn chrono_duration_negative() -> CaseSpec;
443
444 fn std_duration() -> CaseSpec;
448
449 #[cfg(feature = "bytes")]
453 fn bytes_bytes() -> CaseSpec;
454
455 #[cfg(feature = "bytes")]
457 fn bytes_bytes_mut() -> CaseSpec;
458
459 #[cfg(feature = "bytestring")]
461 fn bytestring() -> CaseSpec;
462
463 #[cfg(feature = "compact_str")]
465 fn compact_string() -> CaseSpec;
466
467 #[cfg(feature = "smartstring")]
469 fn smartstring() -> CaseSpec;
470
471 #[cfg(feature = "smol_str")]
473 fn smol_str() -> CaseSpec;
474
475 #[cfg(feature = "iddqd")]
477 fn iddqd_id_hash_map() -> CaseSpec;
478
479 #[cfg(feature = "iddqd")]
481 fn iddqd_id_ord_map() -> CaseSpec;
482
483 #[cfg(feature = "iddqd")]
485 fn iddqd_bi_hash_map() -> CaseSpec;
486
487 #[cfg(feature = "iddqd")]
489 fn iddqd_tri_hash_map() -> CaseSpec;
490
491 #[cfg(feature = "facet-value")]
495 fn value_null() -> CaseSpec;
496
497 #[cfg(feature = "facet-value")]
499 fn value_bool() -> CaseSpec;
500
501 #[cfg(feature = "facet-value")]
503 fn value_integer() -> CaseSpec;
504
505 #[cfg(feature = "facet-value")]
507 fn value_float() -> CaseSpec;
508
509 #[cfg(feature = "facet-value")]
511 fn value_string() -> CaseSpec;
512
513 #[cfg(feature = "facet-value")]
515 fn value_array() -> CaseSpec;
516
517 #[cfg(feature = "facet-value")]
519 fn value_object() -> CaseSpec;
520}
521
522pub fn run_suite<S: FormatSuite + 'static>() {
525 for case in all_cases::<S>() {
526 match case.run() {
527 CaseOutcome::Passed => {}
528 CaseOutcome::Skipped(reason) => {
529 eprintln!(
530 "facet-format-suite: skipping {} for {} ({reason})",
531 case.id,
532 S::format_name()
533 );
534 }
535 CaseOutcome::Failed(msg) => {
536 panic!(
537 "facet-format-suite case {} ({}) failed: {msg}",
538 case.id, case.description
539 );
540 }
541 }
542 }
543}
544
545pub fn all_cases<S: FormatSuite + 'static>() -> Vec<SuiteCase> {
547 vec![
548 SuiteCase::new::<S, StructSingleField>(&CASE_STRUCT_SINGLE_FIELD, S::struct_single_field),
550 SuiteCase::new::<S, Vec<u64>>(&CASE_SEQUENCE_NUMBERS, S::sequence_numbers),
551 SuiteCase::new::<S, Vec<MixedScalar>>(
552 &CASE_SEQUENCE_MIXED_SCALARS,
553 S::sequence_mixed_scalars,
554 ),
555 SuiteCase::new::<S, NestedParent>(&CASE_STRUCT_NESTED, S::struct_nested),
556 SuiteCase::new::<S, ComplexEnum>(&CASE_ENUM_COMPLEX, S::enum_complex),
557 SuiteCase::new::<S, RenamedField>(&CASE_ATTR_RENAME_FIELD, S::attr_rename_field),
559 SuiteCase::new::<S, CamelCaseStruct>(&CASE_ATTR_RENAME_ALL_CAMEL, S::attr_rename_all_camel),
560 SuiteCase::new::<S, WithDefault>(&CASE_ATTR_DEFAULT_FIELD, S::attr_default_field),
561 SuiteCase::new::<S, StructLevelDefault>(&CASE_ATTR_DEFAULT_STRUCT, S::attr_default_struct),
562 SuiteCase::new::<S, WithDefaultFunction>(
563 &CASE_ATTR_DEFAULT_FUNCTION,
564 S::attr_default_function,
565 ),
566 SuiteCase::new::<S, WithOption>(&CASE_OPTION_NONE, S::option_none),
567 SuiteCase::new::<S, WithOption>(&CASE_OPTION_SOME, S::option_some),
568 SuiteCase::new::<S, WithOption>(&CASE_OPTION_NULL, S::option_null),
569 SuiteCase::new::<S, WithSkipSerializing>(
570 &CASE_ATTR_SKIP_SERIALIZING,
571 S::attr_skip_serializing,
572 ),
573 SuiteCase::new::<S, WithSkipSerializingIf>(
574 &CASE_ATTR_SKIP_SERIALIZING_IF,
575 S::attr_skip_serializing_if,
576 ),
577 SuiteCase::new::<S, WithSkip>(&CASE_ATTR_SKIP, S::attr_skip),
578 SuiteCase::new::<S, InternallyTagged>(
580 &CASE_ENUM_INTERNALLY_TAGGED,
581 S::enum_internally_tagged,
582 ),
583 SuiteCase::new::<S, AdjacentlyTagged>(
584 &CASE_ENUM_ADJACENTLY_TAGGED,
585 S::enum_adjacently_tagged,
586 ),
587 SuiteCase::new::<S, FlattenOuter>(&CASE_STRUCT_FLATTEN, S::struct_flatten),
589 SuiteCase::new::<S, UserRecord>(&CASE_TRANSPARENT_NEWTYPE, S::transparent_newtype),
590 SuiteCase::new::<S, FlattenOptionalSome>(
592 &CASE_FLATTEN_OPTIONAL_SOME,
593 S::flatten_optional_some,
594 ),
595 SuiteCase::new::<S, FlattenOptionalNone>(
596 &CASE_FLATTEN_OPTIONAL_NONE,
597 S::flatten_optional_none,
598 ),
599 SuiteCase::new::<S, FlattenOverlapping>(
600 &CASE_FLATTEN_OVERLAPPING_FIELDS_ERROR,
601 S::flatten_overlapping_fields_error,
602 ),
603 SuiteCase::new::<S, FlattenLevel1>(&CASE_FLATTEN_MULTILEVEL, S::flatten_multilevel),
604 SuiteCase::new::<S, FlattenMultipleEnums>(
605 &CASE_FLATTEN_MULTIPLE_ENUMS,
606 S::flatten_multiple_enums,
607 ),
608 SuiteCase::new::<S, DenyUnknownStruct>(&CASE_DENY_UNKNOWN_FIELDS, S::deny_unknown_fields),
610 SuiteCase::new::<S, ExpectsInteger>(
611 &CASE_ERROR_TYPE_MISMATCH_STRING_TO_INT,
612 S::error_type_mismatch_string_to_int,
613 ),
614 SuiteCase::new::<S, ExpectsArray>(
615 &CASE_ERROR_TYPE_MISMATCH_OBJECT_TO_ARRAY,
616 S::error_type_mismatch_object_to_array,
617 ),
618 SuiteCase::new::<S, RequiresAllFields>(
619 &CASE_ERROR_MISSING_REQUIRED_FIELD,
620 S::error_missing_required_field,
621 ),
622 SuiteCase::new::<S, WithAlias>(&CASE_ATTR_ALIAS, S::attr_alias),
624 SuiteCase::new::<S, RenameVsAlias>(
626 &CASE_ATTR_RENAME_VS_ALIAS,
627 S::attr_rename_vs_alias_precedence,
628 ),
629 SuiteCase::new::<S, RenameAllKebab>(&CASE_ATTR_RENAME_ALL_KEBAB, S::attr_rename_all_kebab),
630 SuiteCase::new::<S, RenameAllScreaming>(
631 &CASE_ATTR_RENAME_ALL_SCREAMING,
632 S::attr_rename_all_screaming,
633 ),
634 SuiteCase::new::<S, RenameUnicode>(&CASE_ATTR_RENAME_UNICODE, S::attr_rename_unicode),
635 SuiteCase::new::<S, RenameSpecialChars>(
636 &CASE_ATTR_RENAME_SPECIAL_CHARS,
637 S::attr_rename_special_chars,
638 ),
639 SuiteCase::new::<S, ProxyInt>(&CASE_PROXY_CONTAINER, S::proxy_container),
641 SuiteCase::new::<S, ProxyFieldLevel>(&CASE_PROXY_FIELD_LEVEL, S::proxy_field_level),
642 SuiteCase::new::<S, ProxyInt>(&CASE_PROXY_VALIDATION_ERROR, S::proxy_validation_error),
643 SuiteCase::new::<S, ProxyWithOption>(&CASE_PROXY_WITH_OPTION, S::proxy_with_option),
644 SuiteCase::new::<S, ProxyEnum>(&CASE_PROXY_WITH_ENUM, S::proxy_with_enum),
645 SuiteCase::new::<S, TransparentProxy>(
646 &CASE_PROXY_WITH_TRANSPARENT,
647 S::proxy_with_transparent,
648 ),
649 SuiteCase::new::<S, OpaqueProxyWrapper>(&CASE_OPAQUE_PROXY, S::opaque_proxy),
650 SuiteCase::new::<S, OpaqueProxyOptionWrapper>(
651 &CASE_OPAQUE_PROXY_OPTION,
652 S::opaque_proxy_option,
653 ),
654 SuiteCase::new::<S, OuterTransparent>(
656 &CASE_TRANSPARENT_MULTILEVEL,
657 S::transparent_multilevel,
658 ),
659 SuiteCase::new::<S, TransparentOption>(&CASE_TRANSPARENT_OPTION, S::transparent_option),
660 SuiteCase::new::<S, TransparentNonZero>(&CASE_TRANSPARENT_NONZERO, S::transparent_nonzero),
661 SuiteCase::new::<S, BoolWrapper>(&CASE_SCALAR_BOOL, S::scalar_bool),
663 SuiteCase::new::<S, IntegerTypes>(&CASE_SCALAR_INTEGERS, S::scalar_integers),
664 SuiteCase::new::<S, FloatTypes>(&CASE_SCALAR_FLOATS, S::scalar_floats),
665 SuiteCase::new::<S, FloatTypesScientific>(
666 &CASE_SCALAR_FLOATS_SCIENTIFIC,
667 S::scalar_floats_scientific,
668 ),
669 #[cfg(feature = "net")]
671 SuiteCase::new::<S, IpAddrV4Wrapper>(&CASE_NET_IP_ADDR_V4, S::net_ip_addr_v4),
672 #[cfg(feature = "net")]
673 SuiteCase::new::<S, IpAddrV6Wrapper>(&CASE_NET_IP_ADDR_V6, S::net_ip_addr_v6),
674 #[cfg(feature = "net")]
675 SuiteCase::new::<S, Ipv4AddrWrapper>(&CASE_NET_IPV4_ADDR, S::net_ipv4_addr),
676 #[cfg(feature = "net")]
677 SuiteCase::new::<S, Ipv6AddrWrapper>(&CASE_NET_IPV6_ADDR, S::net_ipv6_addr),
678 #[cfg(feature = "net")]
679 SuiteCase::new::<S, SocketAddrV4Wrapper>(&CASE_NET_SOCKET_ADDR_V4, S::net_socket_addr_v4),
680 #[cfg(feature = "net")]
681 SuiteCase::new::<S, SocketAddrV6Wrapper>(&CASE_NET_SOCKET_ADDR_V6, S::net_socket_addr_v6),
682 #[cfg(feature = "net")]
683 SuiteCase::new::<S, SocketAddrV4ExplicitWrapper>(
684 &CASE_NET_SOCKET_ADDR_V4_EXPLICIT,
685 S::net_socket_addr_v4_explicit,
686 ),
687 #[cfg(feature = "net")]
688 SuiteCase::new::<S, SocketAddrV6ExplicitWrapper>(
689 &CASE_NET_SOCKET_ADDR_V6_EXPLICIT,
690 S::net_socket_addr_v6_explicit,
691 ),
692 SuiteCase::new::<S, MapWrapper>(&CASE_MAP_STRING_KEYS, S::map_string_keys),
694 SuiteCase::new::<S, TupleWrapper>(&CASE_TUPLE_SIMPLE, S::tuple_simple),
695 SuiteCase::new::<S, NestedTupleWrapper>(&CASE_TUPLE_NESTED, S::tuple_nested),
696 SuiteCase::new::<S, EmptyTupleWrapper>(&CASE_TUPLE_EMPTY, S::tuple_empty),
697 SuiteCase::new::<S, SingleElementTupleWrapper>(
698 &CASE_TUPLE_SINGLE_ELEMENT,
699 S::tuple_single_element,
700 ),
701 SuiteCase::new::<S, TupleVariantEnum>(&CASE_TUPLE_STRUCT_VARIANT, S::tuple_struct_variant),
702 SuiteCase::new::<S, NewtypeVariantEnum>(
703 &CASE_TUPLE_NEWTYPE_VARIANT,
704 S::tuple_newtype_variant,
705 ),
706 SuiteCase::new::<S, UnitVariantEnum>(&CASE_ENUM_UNIT_VARIANT, S::enum_unit_variant),
708 SuiteCase::new::<S, NumericEnum>(&CASE_NUMERIC_ENUM, S::numeric_enum),
709 SuiteCase::new::<S, UntaggedEnum>(&CASE_ENUM_UNTAGGED, S::enum_untagged),
710 SuiteCase::new::<S, EnumVariantRename>(&CASE_ENUM_VARIANT_RENAME, S::enum_variant_rename),
711 SuiteCase::new::<S, SignedNumericEnum>(&CASE_SIGNED_NUMERIC_ENUM, S::signed_numeric_enum),
713 SuiteCase::new::<S, NumericEnum>(&CASE_INFERRED_NUMERIC_ENUM, S::inferred_numeric_enum),
714 SuiteCase::new::<S, UntaggedWithNull>(&CASE_UNTAGGED_WITH_NULL, S::untagged_with_null),
716 SuiteCase::new::<S, UntaggedNewtype>(
717 &CASE_UNTAGGED_NEWTYPE_VARIANT,
718 S::untagged_newtype_variant,
719 ),
720 SuiteCase::new::<S, UntaggedAsField>(&CASE_UNTAGGED_AS_FIELD, S::untagged_as_field),
721 SuiteCase::new::<S, UntaggedUnitOnly>(&CASE_UNTAGGED_UNIT_ONLY, S::untagged_unit_only),
722 SuiteCase::new::<S, BoxWrapper>(&CASE_BOX_WRAPPER, S::box_wrapper),
724 SuiteCase::new::<S, ArcWrapper>(&CASE_ARC_WRAPPER, S::arc_wrapper),
725 SuiteCase::new::<S, RcWrapper>(&CASE_RC_WRAPPER, S::rc_wrapper),
726 SuiteCase::new::<S, BoxStrWrapper>(&CASE_BOX_STR, S::box_str),
727 SuiteCase::new::<S, ArcStrWrapper>(&CASE_ARC_STR, S::arc_str),
728 SuiteCase::new::<S, RcStrWrapper>(&CASE_RC_STR, S::rc_str),
729 SuiteCase::new::<S, ArcSliceWrapper>(&CASE_ARC_SLICE, S::arc_slice),
730 #[cfg(feature = "yoke")]
732 SuiteCase::new::<S, YokeWrapper>(&CASE_YOKE_COW_STR, S::yoke_cow_str),
733 #[cfg(feature = "yoke")]
734 SuiteCase::new::<S, YokingWrapper>(&CASE_YOKE_CUSTOM, S::yoke_custom),
735 SuiteCase::new::<S, SetWrapper>(&CASE_SET_BTREE, S::set_btree),
737 SuiteCase::new::<S, IntegerTypes16>(&CASE_SCALAR_INTEGERS_16, S::scalar_integers_16),
739 SuiteCase::new::<S, IntegerTypes128>(&CASE_SCALAR_INTEGERS_128, S::scalar_integers_128),
740 SuiteCase::new::<S, IntegerTypesSize>(&CASE_SCALAR_INTEGERS_SIZE, S::scalar_integers_size),
741 SuiteCase::new::<S, NonZeroTypes>(&CASE_NONZERO_INTEGERS, S::nonzero_integers),
743 SuiteCase::new::<S, NonZeroTypesExtended>(
744 &CASE_NONZERO_INTEGERS_EXTENDED,
745 S::nonzero_integers_extended,
746 ),
747 SuiteCase::new::<S, CowStrWrapper>(&CASE_COW_STR, S::cow_str),
749 SuiteCase::new::<S, BytesWrapper>(&CASE_BYTES_VEC_U8, S::bytes_vec_u8),
751 SuiteCase::new::<S, ArrayWrapper>(&CASE_ARRAY_FIXED_SIZE, S::array_fixed_size),
753 SuiteCase::new::<S, SkipUnknownStruct>(&CASE_SKIP_UNKNOWN_FIELDS, S::skip_unknown_fields),
755 SuiteCase::new::<S, StringEscapes>(&CASE_STRING_ESCAPES, S::string_escapes),
757 SuiteCase::new::<S, StringEscapesExtended>(
758 &CASE_STRING_ESCAPES_EXTENDED,
759 S::string_escapes_extended,
760 ),
761 SuiteCase::new::<S, UnitStruct>(&CASE_UNIT_STRUCT, S::unit_struct),
763 SuiteCase::new::<S, NewtypeU64Wrapper>(&CASE_NEWTYPE_U64, S::newtype_u64),
765 SuiteCase::new::<S, NewtypeStringWrapper>(&CASE_NEWTYPE_STRING, S::newtype_string),
766 SuiteCase::new::<S, CharWrapper>(&CASE_CHAR_SCALAR, S::char_scalar),
768 SuiteCase::new::<S, HashSetWrapper>(&CASE_HASHSET, S::hashset),
770 SuiteCase::new::<S, NestedVecWrapper>(&CASE_VEC_NESTED, S::vec_nested),
772 #[cfg(feature = "uuid")]
774 SuiteCase::new::<S, UuidWrapper>(&CASE_UUID, S::uuid),
775 #[cfg(feature = "ulid")]
776 SuiteCase::new::<S, UlidWrapper>(&CASE_ULID, S::ulid),
777 #[cfg(feature = "camino")]
778 SuiteCase::new::<S, CaminoWrapper>(&CASE_CAMINO_PATH, S::camino_path),
779 #[cfg(feature = "ordered-float")]
780 SuiteCase::new::<S, OrderedFloatWrapper>(&CASE_ORDERED_FLOAT, S::ordered_float),
781 #[cfg(feature = "rust_decimal")]
782 SuiteCase::new::<S, RustDecimalWrapper>(&CASE_RUST_DECIMAL, S::rust_decimal),
783 #[cfg(feature = "time")]
784 SuiteCase::new::<S, TimeOffsetDateTimeWrapper>(
785 &CASE_TIME_OFFSET_DATETIME,
786 S::time_offset_datetime,
787 ),
788 #[cfg(feature = "jiff02")]
789 SuiteCase::new::<S, JiffTimestampWrapper>(&CASE_JIFF_TIMESTAMP, S::jiff_timestamp),
790 #[cfg(feature = "jiff02")]
791 SuiteCase::new::<S, JiffCivilDateTimeWrapper>(
792 &CASE_JIFF_CIVIL_DATETIME,
793 S::jiff_civil_datetime,
794 ),
795 #[cfg(feature = "chrono")]
796 SuiteCase::new::<S, ChronoDateTimeUtcWrapper>(
797 &CASE_CHRONO_DATETIME_UTC,
798 S::chrono_datetime_utc,
799 ),
800 #[cfg(feature = "chrono")]
801 SuiteCase::new::<S, ChronoNaiveDateTimeWrapper>(
802 &CASE_CHRONO_NAIVE_DATETIME,
803 S::chrono_naive_datetime,
804 ),
805 #[cfg(feature = "chrono")]
806 SuiteCase::new::<S, ChronoNaiveDateWrapper>(&CASE_CHRONO_NAIVE_DATE, S::chrono_naive_date),
807 #[cfg(feature = "chrono")]
808 SuiteCase::new::<S, ChronoNaiveTimeWrapper>(&CASE_CHRONO_NAIVE_TIME, S::chrono_naive_time),
809 #[cfg(feature = "chrono")]
810 SuiteCase::new::<S, ChronoInVecWrapper>(&CASE_CHRONO_IN_VEC, S::chrono_in_vec),
811 #[cfg(feature = "chrono")]
812 SuiteCase::new::<S, ChronoDurationWrapper>(&CASE_CHRONO_DURATION, S::chrono_duration),
813 #[cfg(feature = "chrono")]
814 SuiteCase::new::<S, ChronoDurationNegativeWrapper>(
815 &CASE_CHRONO_DURATION_NEGATIVE,
816 S::chrono_duration_negative,
817 ),
818 SuiteCase::new::<S, StdDurationWrapper>(&CASE_STD_DURATION, S::std_duration),
820 #[cfg(feature = "bytes")]
822 SuiteCase::new::<S, BytesBytesWrapper>(&CASE_BYTES_BYTES, S::bytes_bytes),
823 #[cfg(feature = "bytes")]
824 SuiteCase::new::<S, BytesBytesMutWrapper>(&CASE_BYTES_BYTES_MUT, S::bytes_bytes_mut),
825 #[cfg(feature = "bytestring")]
827 SuiteCase::new::<S, ByteStringWrapper>(&CASE_BYTESTRING, S::bytestring),
828 #[cfg(feature = "compact_str")]
829 SuiteCase::new::<S, CompactStringWrapper>(&CASE_COMPACT_STRING, S::compact_string),
830 #[cfg(feature = "smartstring")]
831 SuiteCase::new::<S, SmartStringWrapper>(&CASE_SMARTSTRING, S::smartstring),
832 #[cfg(feature = "smol_str")]
833 SuiteCase::new::<S, SmolStrWrapper>(&CASE_SMOL_STR, S::smol_str),
834 #[cfg(feature = "iddqd")]
835 SuiteCase::new::<S, IddqdIdHashMapWrapper>(&CASE_IDDQD_ID_HASH_MAP, S::iddqd_id_hash_map),
836 #[cfg(feature = "iddqd")]
837 SuiteCase::new::<S, IddqdIdOrdMapWrapper>(&CASE_IDDQD_ID_ORD_MAP, S::iddqd_id_ord_map),
838 #[cfg(feature = "iddqd")]
839 SuiteCase::new::<S, IddqdBiHashMapWrapper>(&CASE_IDDQD_BI_HASH_MAP, S::iddqd_bi_hash_map),
840 #[cfg(feature = "iddqd")]
841 SuiteCase::new::<S, IddqdTriHashMapWrapper>(
842 &CASE_IDDQD_TRI_HASH_MAP,
843 S::iddqd_tri_hash_map,
844 ),
845 #[cfg(feature = "facet-value")]
847 SuiteCase::new::<S, facet_value::Value>(&CASE_VALUE_NULL, S::value_null),
848 #[cfg(feature = "facet-value")]
849 SuiteCase::new::<S, facet_value::Value>(&CASE_VALUE_BOOL, S::value_bool),
850 #[cfg(feature = "facet-value")]
851 SuiteCase::new::<S, facet_value::Value>(&CASE_VALUE_INTEGER, S::value_integer),
852 #[cfg(feature = "facet-value")]
853 SuiteCase::new::<S, facet_value::Value>(&CASE_VALUE_FLOAT, S::value_float),
854 #[cfg(feature = "facet-value")]
855 SuiteCase::new::<S, facet_value::Value>(&CASE_VALUE_STRING, S::value_string),
856 #[cfg(feature = "facet-value")]
857 SuiteCase::new::<S, facet_value::Value>(&CASE_VALUE_ARRAY, S::value_array),
858 #[cfg(feature = "facet-value")]
859 SuiteCase::new::<S, facet_value::Value>(&CASE_VALUE_OBJECT, S::value_object),
860 ]
861}
862
863#[derive(Debug, Clone, Copy, Default)]
865pub enum CompareMode {
866 #[default]
868 Reflection,
869 PartialEq,
871}
872
873#[derive(Debug, Clone)]
875pub struct CaseSpec {
876 payload: CasePayload,
877 note: Option<&'static str>,
878 roundtrip: RoundtripSpec,
879 compare_mode: CompareMode,
880}
881
882impl CaseSpec {
883 pub const fn from_bytes(input: &'static [u8]) -> Self {
885 Self {
886 payload: CasePayload::Input(input),
887 note: None,
888 roundtrip: RoundtripSpec::Enabled,
889 compare_mode: CompareMode::Reflection,
890 }
891 }
892
893 #[allow(clippy::should_implement_trait)]
895 pub const fn from_str(input: &'static str) -> Self {
896 Self::from_bytes(input.as_bytes())
897 }
898
899 pub const fn skip(reason: &'static str) -> Self {
901 Self {
902 payload: CasePayload::Skip { reason },
903 note: None,
904 roundtrip: RoundtripSpec::Enabled,
905 compare_mode: CompareMode::Reflection,
906 }
907 }
908
909 pub const fn with_note(mut self, note: &'static str) -> Self {
911 self.note = Some(note);
912 self
913 }
914
915 pub const fn without_roundtrip(mut self, reason: &'static str) -> Self {
917 self.roundtrip = RoundtripSpec::Disabled { reason };
918 self
919 }
920
921 pub const fn with_partial_eq(mut self) -> Self {
924 self.compare_mode = CompareMode::PartialEq;
925 self
926 }
927
928 pub const fn expect_error(input: &'static str, error_contains: &'static str) -> Self {
930 Self {
931 payload: CasePayload::ExpectError {
932 input: input.as_bytes(),
933 error_contains,
934 },
935 note: None,
936 roundtrip: RoundtripSpec::Disabled {
937 reason: "error case",
938 },
939 compare_mode: CompareMode::Reflection,
940 }
941 }
942
943 pub const fn from_bytes_vec(input: Vec<u8>) -> Self {
948 Self {
949 payload: CasePayload::DynamicInput(input),
950 note: None,
951 roundtrip: RoundtripSpec::Enabled,
952 compare_mode: CompareMode::Reflection,
953 }
954 }
955}
956
957#[derive(Debug, Clone)]
958enum CasePayload {
959 Input(&'static [u8]),
960 DynamicInput(Vec<u8>),
962 Skip {
963 reason: &'static str,
964 },
965 ExpectError {
967 input: &'static [u8],
968 error_contains: &'static str,
969 },
970}
971
972#[derive(Debug, Clone)]
973enum RoundtripSpec {
974 Enabled,
975 Disabled { reason: &'static str },
976}
977
978struct CaseDescriptor<T> {
979 id: &'static str,
980 description: &'static str,
981 expected: fn() -> T,
982}
983
984#[derive(Debug)]
985pub enum CaseOutcome {
986 Passed,
987 Skipped(&'static str),
988 Failed(String),
989}
990
991pub struct SuiteCase {
992 pub id: &'static str,
993 pub description: &'static str,
994 skip_reason: Option<&'static str>,
995 runner: Box<dyn Fn() -> CaseOutcome + Send + Sync + 'static>,
996 #[cfg(feature = "tokio")]
997 async_runner: Box<dyn Fn() -> CaseOutcome + Send + Sync + 'static>,
998}
999
1000impl SuiteCase {
1001 #[cfg(not(feature = "tokio"))]
1002 fn new<S, T>(desc: &'static CaseDescriptor<T>, provider: fn() -> CaseSpec) -> Self
1003 where
1004 S: FormatSuite,
1005 for<'facet> T: Facet<'facet>,
1006 T: Debug + PartialEq + 'static,
1007 {
1008 let spec = provider();
1009 let skip_reason = match spec.payload {
1010 CasePayload::Skip { reason } => Some(reason),
1011 _ => None,
1012 };
1013 let runner_spec = spec.clone();
1014 let runner = move || execute_case::<S, T>(desc, runner_spec.clone());
1015
1016 Self {
1017 id: desc.id,
1018 description: desc.description,
1019 skip_reason,
1020 runner: Box::new(runner),
1021 }
1022 }
1023
1024 #[cfg(feature = "tokio")]
1025 fn new<S, T>(desc: &'static CaseDescriptor<T>, provider: fn() -> CaseSpec) -> Self
1026 where
1027 S: FormatSuite + 'static,
1028 for<'facet> T: Facet<'facet>,
1029 T: Debug + PartialEq + 'static,
1030 {
1031 let spec = provider();
1032 let skip_reason = match spec.payload {
1033 CasePayload::Skip { reason } => Some(reason),
1034 _ => None,
1035 };
1036 let runner_spec = spec.clone();
1037 let runner = move || execute_case::<S, T>(desc, runner_spec.clone());
1038
1039 #[cfg(feature = "tokio")]
1040 let async_runner = {
1041 let async_spec = spec.clone();
1042 Box::new(move || {
1043 let spec = async_spec.clone();
1044 let rt = tokio::runtime::Builder::new_current_thread()
1046 .enable_all()
1047 .build()
1048 .expect("failed to create tokio runtime");
1049 rt.block_on(execute_case_async::<S, T>(desc, spec))
1050 }) as Box<dyn Fn() -> CaseOutcome + Send + Sync + 'static>
1051 };
1052
1053 Self {
1054 id: desc.id,
1055 description: desc.description,
1056 skip_reason,
1057 runner: Box::new(runner),
1058 #[cfg(feature = "tokio")]
1059 async_runner,
1060 }
1061 }
1062
1063 pub fn run(&self) -> CaseOutcome {
1064 (self.runner)()
1065 }
1066
1067 #[cfg(feature = "tokio")]
1070 pub fn run_async(&self) -> CaseOutcome {
1071 (self.async_runner)()
1072 }
1073
1074 pub const fn skip_reason(&self) -> Option<&'static str> {
1075 self.skip_reason
1076 }
1077}
1078
1079fn execute_case<S, T>(desc: &'static CaseDescriptor<T>, spec: CaseSpec) -> CaseOutcome
1080where
1081 S: FormatSuite,
1082 for<'facet> T: Facet<'facet>,
1083 T: Debug + PartialEq,
1084{
1085 let note = spec.note;
1086 let compare_mode = spec.compare_mode;
1087 let roundtrip_disabled_reason = match spec.roundtrip {
1088 RoundtripSpec::Enabled => None,
1089 RoundtripSpec::Disabled { reason } => Some(reason),
1090 };
1091 let highlight_language = S::highlight_language();
1092 match spec.payload {
1093 CasePayload::Skip { reason } => CaseOutcome::Skipped(reason),
1094 CasePayload::Input(input) => {
1095 let expected = (desc.expected)();
1096 tracing::info!("=================== Deserializing");
1097 let actual = match S::deserialize::<T>(input) {
1098 Ok(value) => value,
1099 Err(err) => return CaseOutcome::Failed(err.to_string()),
1100 };
1101
1102 tracing::info!("=================== Emitting showcase");
1103 emit_case_showcase::<S, T>(
1104 desc,
1105 note,
1106 roundtrip_disabled_reason,
1107 input,
1108 highlight_language,
1109 &actual,
1110 );
1111
1112 tracing::info!("=================== Comparing deserialized value");
1113 let first_assert = panic::catch_unwind(AssertUnwindSafe(|| match compare_mode {
1115 CompareMode::Reflection => {
1116 assert_same!(
1117 actual,
1118 expected,
1119 "facet-format-suite {} ({}) produced unexpected value",
1120 desc.id,
1121 desc.description
1122 );
1123 }
1124 CompareMode::PartialEq => {
1125 assert_eq!(
1126 actual, expected,
1127 "facet-format-suite {} ({}) produced unexpected value",
1128 desc.id, desc.description
1129 );
1130 }
1131 }));
1132 if let Err(payload) = first_assert {
1133 return CaseOutcome::Failed(format_panic(payload));
1134 }
1135
1136 if roundtrip_disabled_reason.is_some() {
1137 return CaseOutcome::Passed;
1138 }
1139
1140 tracing::info!("=================== Serializing");
1141 let Some(serialized) = S::serialize(&actual) else {
1142 return CaseOutcome::Passed;
1143 };
1144
1145 let serialized = match serialized {
1146 Ok(bytes) => bytes,
1147 Err(msg) => {
1148 return CaseOutcome::Failed(format!(
1149 "facet-format-suite {} ({}) serialization failed: {msg}",
1150 desc.id, desc.description
1151 ));
1152 }
1153 };
1154
1155 if let Ok(serialized) = std::str::from_utf8(&serialized) {
1156 tracing::info!(%serialized, "=================== Serialized result");
1157 }
1158
1159 let roundtripped = match S::deserialize::<T>(&serialized) {
1160 Ok(value) => value,
1161 Err(err) => {
1162 return CaseOutcome::Failed(format!(
1163 "facet-format-suite {} ({}) round-trip deserialize failed: {err}",
1164 desc.id, desc.description
1165 ));
1166 }
1167 };
1168
1169 match panic::catch_unwind(AssertUnwindSafe(|| match compare_mode {
1171 CompareMode::Reflection => {
1172 assert_same!(
1173 roundtripped,
1174 actual,
1175 "facet-format-suite {} ({}) round-trip mismatch",
1176 desc.id,
1177 desc.description
1178 );
1179 }
1180 CompareMode::PartialEq => {
1181 assert_eq!(
1182 roundtripped, actual,
1183 "facet-format-suite {} ({}) round-trip mismatch",
1184 desc.id, desc.description
1185 );
1186 }
1187 })) {
1188 Ok(_) => CaseOutcome::Passed,
1189 Err(payload) => CaseOutcome::Failed(format_panic(payload)),
1190 }
1191 }
1192 CasePayload::DynamicInput(ref input) => {
1193 let expected = (desc.expected)();
1194 let actual = match S::deserialize::<T>(input) {
1195 Ok(value) => value,
1196 Err(err) => return CaseOutcome::Failed(err.to_string()),
1197 };
1198
1199 emit_case_showcase_dynamic::<S, T>(
1200 desc,
1201 note,
1202 roundtrip_disabled_reason,
1203 input,
1204 &actual,
1205 );
1206
1207 let first_assert = panic::catch_unwind(AssertUnwindSafe(|| match compare_mode {
1209 CompareMode::Reflection => {
1210 assert_same!(
1211 actual,
1212 expected,
1213 "facet-format-suite {} ({}) produced unexpected value",
1214 desc.id,
1215 desc.description
1216 );
1217 }
1218 CompareMode::PartialEq => {
1219 assert_eq!(
1220 actual, expected,
1221 "facet-format-suite {} ({}) produced unexpected value",
1222 desc.id, desc.description
1223 );
1224 }
1225 }));
1226 if let Err(payload) = first_assert {
1227 return CaseOutcome::Failed(format_panic(payload));
1228 }
1229
1230 if roundtrip_disabled_reason.is_some() {
1231 return CaseOutcome::Passed;
1232 }
1233
1234 let Some(serialized) = S::serialize(&actual) else {
1235 return CaseOutcome::Passed;
1236 };
1237
1238 let serialized = match serialized {
1239 Ok(bytes) => bytes,
1240 Err(msg) => {
1241 return CaseOutcome::Failed(format!(
1242 "facet-format-suite {} ({}) serialization failed: {msg}",
1243 desc.id, desc.description
1244 ));
1245 }
1246 };
1247
1248 let roundtripped = match S::deserialize::<T>(&serialized) {
1249 Ok(value) => value,
1250 Err(err) => {
1251 return CaseOutcome::Failed(format!(
1252 "facet-format-suite {} ({}) round-trip deserialize failed: {err}",
1253 desc.id, desc.description
1254 ));
1255 }
1256 };
1257
1258 match panic::catch_unwind(AssertUnwindSafe(|| match compare_mode {
1260 CompareMode::Reflection => {
1261 assert_same!(
1262 roundtripped,
1263 actual,
1264 "facet-format-suite {} ({}) round-trip mismatch",
1265 desc.id,
1266 desc.description
1267 );
1268 }
1269 CompareMode::PartialEq => {
1270 assert_eq!(
1271 roundtripped, actual,
1272 "facet-format-suite {} ({}) round-trip mismatch",
1273 desc.id, desc.description
1274 );
1275 }
1276 })) {
1277 Ok(_) => CaseOutcome::Passed,
1278 Err(payload) => CaseOutcome::Failed(format_panic(payload)),
1279 }
1280 }
1281 CasePayload::ExpectError {
1282 input,
1283 error_contains,
1284 } => {
1285 emit_error_case_showcase::<S>(
1286 desc.id,
1287 desc.description,
1288 note,
1289 input,
1290 highlight_language,
1291 error_contains,
1292 );
1293
1294 match S::deserialize::<T>(input) {
1295 Ok(_) => CaseOutcome::Failed(format!(
1296 "facet-format-suite {} ({}) expected error containing '{}' but deserialization succeeded",
1297 desc.id, desc.description, error_contains
1298 )),
1299 Err(err) => {
1300 let err_str = err.to_string();
1301 if err_str.contains(error_contains) {
1302 CaseOutcome::Passed
1303 } else {
1304 CaseOutcome::Failed(format!(
1305 "facet-format-suite {} ({}) expected error containing '{}' but got: {}",
1306 desc.id, desc.description, error_contains, err_str
1307 ))
1308 }
1309 }
1310 }
1311 }
1312 }
1313}
1314
1315fn format_panic(payload: Box<dyn Any + Send>) -> String {
1316 if let Some(msg) = payload.downcast_ref::<&str>() {
1317 msg.to_string()
1318 } else if let Some(msg) = payload.downcast_ref::<String>() {
1319 msg.clone()
1320 } else {
1321 "panic with non-string payload".into()
1322 }
1323}
1324
1325#[cfg(feature = "tokio")]
1326async fn execute_case_async<S, T>(desc: &'static CaseDescriptor<T>, spec: CaseSpec) -> CaseOutcome
1327where
1328 S: FormatSuite,
1329 for<'facet> T: Facet<'facet>,
1330 T: Debug + PartialEq,
1331{
1332 let compare_mode = spec.compare_mode;
1333 match spec.payload {
1334 CasePayload::Skip { reason } => CaseOutcome::Skipped(reason),
1335 CasePayload::Input(input) => {
1336 let result = S::deserialize_async::<T>(input).await;
1338 let actual = match result {
1339 None => {
1340 return CaseOutcome::Skipped("async deserialization not supported");
1342 }
1343 Some(Ok(value)) => value,
1344 Some(Err(err)) => return CaseOutcome::Failed(err.to_string()),
1345 };
1346
1347 let expected = (desc.expected)();
1348
1349 let first_assert = panic::catch_unwind(AssertUnwindSafe(|| match compare_mode {
1351 CompareMode::Reflection => {
1352 assert_same!(
1353 actual,
1354 expected,
1355 "facet-format-suite {} ({}) async produced unexpected value",
1356 desc.id,
1357 desc.description
1358 );
1359 }
1360 CompareMode::PartialEq => {
1361 assert_eq!(
1362 actual, expected,
1363 "facet-format-suite {} ({}) async produced unexpected value",
1364 desc.id, desc.description
1365 );
1366 }
1367 }));
1368 if let Err(payload) = first_assert {
1369 return CaseOutcome::Failed(format_panic(payload));
1370 }
1371
1372 CaseOutcome::Passed
1374 }
1375 CasePayload::DynamicInput(ref input) => {
1376 let result = S::deserialize_async::<T>(input).await;
1378 let actual = match result {
1379 None => {
1380 return CaseOutcome::Skipped("async deserialization not supported");
1382 }
1383 Some(Ok(value)) => value,
1384 Some(Err(err)) => return CaseOutcome::Failed(err.to_string()),
1385 };
1386
1387 let expected = (desc.expected)();
1388
1389 let first_assert = panic::catch_unwind(AssertUnwindSafe(|| match compare_mode {
1391 CompareMode::Reflection => {
1392 assert_same!(
1393 actual,
1394 expected,
1395 "facet-format-suite {} ({}) async produced unexpected value",
1396 desc.id,
1397 desc.description
1398 );
1399 }
1400 CompareMode::PartialEq => {
1401 assert_eq!(
1402 actual, expected,
1403 "facet-format-suite {} ({}) async produced unexpected value",
1404 desc.id, desc.description
1405 );
1406 }
1407 }));
1408 if let Err(payload) = first_assert {
1409 return CaseOutcome::Failed(format_panic(payload));
1410 }
1411
1412 CaseOutcome::Passed
1414 }
1415 CasePayload::ExpectError {
1416 input,
1417 error_contains,
1418 } => {
1419 let result = S::deserialize_async::<T>(input).await;
1420 match result {
1421 None => CaseOutcome::Skipped("async deserialization not supported"),
1422 Some(Ok(_)) => CaseOutcome::Failed(format!(
1423 "facet-format-suite {} ({}) async expected error containing '{}' but deserialization succeeded",
1424 desc.id, desc.description, error_contains
1425 )),
1426 Some(Err(err)) => {
1427 let err_str = err.to_string();
1428 if err_str.contains(error_contains) {
1429 CaseOutcome::Passed
1430 } else {
1431 CaseOutcome::Failed(format!(
1432 "facet-format-suite {} ({}) async expected error containing '{}' but got: {}",
1433 desc.id, desc.description, error_contains, err_str
1434 ))
1435 }
1436 }
1437 }
1438 }
1439 }
1440}
1441
1442const CASE_STRUCT_SINGLE_FIELD: CaseDescriptor<StructSingleField> = CaseDescriptor {
1443 id: "struct::single_field",
1444 description: "single-field object parsed into StructSingleField",
1445 expected: || StructSingleField {
1446 name: "facet".into(),
1447 },
1448};
1449
1450const CASE_SEQUENCE_NUMBERS: CaseDescriptor<Vec<u64>> = CaseDescriptor {
1451 id: "sequence::numbers",
1452 description: "array of unsigned integers parsed into Vec<u64>",
1453 expected: || vec![1, 2, 3],
1454};
1455
1456const CASE_SEQUENCE_MIXED_SCALARS: CaseDescriptor<Vec<MixedScalar>> = CaseDescriptor {
1457 id: "sequence::mixed_scalars",
1458 description: "array of heterogeneous scalars parsed into Vec<MixedScalar>",
1459 expected: || {
1460 vec![
1461 MixedScalar::Signed(-1),
1462 MixedScalar::Float(4.625),
1463 MixedScalar::Null,
1464 MixedScalar::Bool(true),
1465 ]
1466 },
1467};
1468
1469const CASE_STRUCT_NESTED: CaseDescriptor<NestedParent> = CaseDescriptor {
1470 id: "struct::nested",
1471 description: "struct containing nested child and tag list",
1472 expected: || NestedParent {
1473 id: 42,
1474 child: NestedChild {
1475 code: "alpha".into(),
1476 active: true,
1477 },
1478 tags: vec!["core".into(), "json".into()],
1479 },
1480};
1481
1482const CASE_ENUM_COMPLEX: CaseDescriptor<ComplexEnum> = CaseDescriptor {
1483 id: "enum::complex",
1484 description: "enum with unit, tuple, and struct variants",
1485 expected: || ComplexEnum::Label {
1486 name: "facet".into(),
1487 level: 7,
1488 },
1489};
1490
1491const CASE_ATTR_RENAME_FIELD: CaseDescriptor<RenamedField> = CaseDescriptor {
1494 id: "attr::rename_field",
1495 description: "field with #[facet(rename = \"userName\")]",
1496 expected: || RenamedField {
1497 user_name: "alice".into(),
1498 age: 30,
1499 },
1500};
1501
1502const CASE_ATTR_RENAME_ALL_CAMEL: CaseDescriptor<CamelCaseStruct> = CaseDescriptor {
1503 id: "attr::rename_all_camel",
1504 description: "struct with #[facet(rename_all = \"camelCase\")]",
1505 expected: || CamelCaseStruct {
1506 first_name: "Jane".into(),
1507 last_name: "Doe".into(),
1508 is_active: true,
1509 },
1510};
1511
1512const CASE_ATTR_DEFAULT_FIELD: CaseDescriptor<WithDefault> = CaseDescriptor {
1513 id: "attr::default_field",
1514 description: "field with #[facet(default)] missing from input",
1515 expected: || WithDefault {
1516 required: "present".into(),
1517 optional_count: 0, },
1519};
1520
1521const CASE_ATTR_DEFAULT_STRUCT: CaseDescriptor<StructLevelDefault> = CaseDescriptor {
1522 id: "attr::default_struct",
1523 description: "struct-level #[facet(default)] with missing field uses Default",
1524 expected: || StructLevelDefault {
1525 count: 123,
1526 message: String::new(), },
1528};
1529
1530const CASE_ATTR_DEFAULT_FUNCTION: CaseDescriptor<WithDefaultFunction> = CaseDescriptor {
1531 id: "attr::default_function",
1532 description: "field with #[facet(default = expr)] uses custom default",
1533 expected: || WithDefaultFunction {
1534 magic_number: 42, name: "hello".into(),
1536 },
1537};
1538
1539const CASE_OPTION_NONE: CaseDescriptor<WithOption> = CaseDescriptor {
1540 id: "option::none",
1541 description: "Option<T> field missing from input becomes None",
1542 expected: || WithOption {
1543 name: "test".into(),
1544 nickname: None,
1545 },
1546};
1547
1548const CASE_OPTION_SOME: CaseDescriptor<WithOption> = CaseDescriptor {
1549 id: "option::some",
1550 description: "Option<T> field with Some value",
1551 expected: || WithOption {
1552 name: "test".into(),
1553 nickname: Some("nick".into()),
1554 },
1555};
1556
1557const CASE_OPTION_NULL: CaseDescriptor<WithOption> = CaseDescriptor {
1558 id: "option::null",
1559 description: "Option<T> field with explicit null becomes None",
1560 expected: || WithOption {
1561 name: "test".into(),
1562 nickname: None,
1563 },
1564};
1565
1566const CASE_ATTR_SKIP_SERIALIZING: CaseDescriptor<WithSkipSerializing> = CaseDescriptor {
1567 id: "attr::skip_serializing",
1568 description: "field with #[facet(skip_serializing)] not in output",
1569 expected: || WithSkipSerializing {
1570 visible: "shown".into(),
1571 hidden: String::new(), },
1573};
1574
1575const CASE_ATTR_SKIP_SERIALIZING_IF: CaseDescriptor<WithSkipSerializingIf> = CaseDescriptor {
1576 id: "attr::skip_serializing_if",
1577 description: "field with #[facet(skip_serializing_if = pred)] skipped when pred is true",
1578 expected: || WithSkipSerializingIf {
1579 name: "test".into(),
1580 optional_data: None, },
1582};
1583
1584const CASE_ATTR_SKIP: CaseDescriptor<WithSkip> = CaseDescriptor {
1585 id: "attr::skip",
1586 description: "field with #[facet(skip)] ignored for both ser and de",
1587 expected: || WithSkip {
1588 visible: "data".into(),
1589 internal: 0, },
1591};
1592
1593const CASE_ENUM_INTERNALLY_TAGGED: CaseDescriptor<InternallyTagged> = CaseDescriptor {
1596 id: "enum::internally_tagged",
1597 description: "internally tagged enum with #[facet(tag = \"type\")]",
1598 expected: || InternallyTagged::Circle { radius: 5.0 },
1599};
1600
1601const CASE_ENUM_ADJACENTLY_TAGGED: CaseDescriptor<AdjacentlyTagged> = CaseDescriptor {
1602 id: "enum::adjacently_tagged",
1603 description: "adjacently tagged enum with #[facet(tag = \"t\", content = \"c\")]",
1604 expected: || AdjacentlyTagged::Message("hello".into()),
1605};
1606
1607const CASE_STRUCT_FLATTEN: CaseDescriptor<FlattenOuter> = CaseDescriptor {
1610 id: "struct::flatten",
1611 description: "struct with #[facet(flatten)] flattening inner fields",
1612 expected: || FlattenOuter {
1613 name: "point".into(),
1614 coords: FlattenInner { x: 10, y: 20 },
1615 },
1616};
1617
1618const CASE_TRANSPARENT_NEWTYPE: CaseDescriptor<UserRecord> = CaseDescriptor {
1619 id: "attr::transparent",
1620 description: "struct containing #[facet(transparent)] newtype",
1621 expected: || UserRecord {
1622 id: UserId(42),
1623 name: "alice".into(),
1624 },
1625};
1626
1627const CASE_FLATTEN_OPTIONAL_SOME: CaseDescriptor<FlattenOptionalSome> = CaseDescriptor {
1630 id: "flatten::optional_some",
1631 description: "flattened field is Option<T> with Some value",
1632 expected: || FlattenOptionalSome {
1633 name: "test".into(),
1634 metadata: Some(FlattenMetadata {
1635 version: 1,
1636 author: "alice".into(),
1637 }),
1638 },
1639};
1640
1641const CASE_FLATTEN_OPTIONAL_NONE: CaseDescriptor<FlattenOptionalNone> = CaseDescriptor {
1642 id: "flatten::optional_none",
1643 description: "flattened field is Option<T> with None value",
1644 expected: || FlattenOptionalNone {
1645 name: "test".into(),
1646 metadata: None,
1647 },
1648};
1649
1650const CASE_FLATTEN_OVERLAPPING_FIELDS_ERROR: CaseDescriptor<FlattenOverlapping> = CaseDescriptor {
1651 id: "flatten::overlapping_fields_error",
1652 description: "two flattened structs with overlapping field names (error)",
1653 expected: || FlattenOverlapping {
1654 part_a: FlattenPartA {
1655 field_a: "a".into(),
1656 shared: 1,
1657 },
1658 part_b: FlattenPartB {
1659 field_b: "b".into(),
1660 shared: 2,
1661 },
1662 },
1663};
1664
1665const CASE_FLATTEN_MULTILEVEL: CaseDescriptor<FlattenLevel1> = CaseDescriptor {
1666 id: "flatten::multilevel",
1667 description: "three levels of nested flatten (A -> B -> C, all flattened)",
1668 expected: || FlattenLevel1 {
1669 top_field: "top".into(),
1670 level2: FlattenLevel2 {
1671 mid_field: 42,
1672 level3: FlattenLevel3 { deep_field: 100 },
1673 },
1674 },
1675};
1676
1677const CASE_FLATTEN_MULTIPLE_ENUMS: CaseDescriptor<FlattenMultipleEnums> = CaseDescriptor {
1678 id: "flatten::multiple_enums",
1679 description: "two different enums flattened into same struct",
1680 expected: || FlattenMultipleEnums {
1681 name: "service".into(),
1682 auth: FlattenAuthMethod::Password(FlattenAuthPassword {
1683 password: "secret".into(),
1684 }),
1685 transport: FlattenTransport::Tcp(FlattenTransportTcp { port: 8080 }),
1686 },
1687};
1688
1689const CASE_DENY_UNKNOWN_FIELDS: CaseDescriptor<DenyUnknownStruct> = CaseDescriptor {
1692 id: "error::deny_unknown_fields",
1693 description: "#[facet(deny_unknown_fields)] rejects input with extra fields",
1694 expected: || DenyUnknownStruct {
1695 foo: "abc".into(),
1696 bar: 42,
1697 },
1698};
1699
1700const CASE_ERROR_TYPE_MISMATCH_STRING_TO_INT: CaseDescriptor<ExpectsInteger> = CaseDescriptor {
1701 id: "error::type_mismatch_string_to_int",
1702 description: "type mismatch - string provided where integer expected",
1703 expected: || ExpectsInteger { value: 42 },
1704};
1705
1706const CASE_ERROR_TYPE_MISMATCH_OBJECT_TO_ARRAY: CaseDescriptor<ExpectsArray> = CaseDescriptor {
1707 id: "error::type_mismatch_object_to_array",
1708 description: "structure mismatch - object provided where array expected",
1709 expected: || ExpectsArray {
1710 items: vec![1, 2, 3],
1711 },
1712};
1713
1714const CASE_ERROR_MISSING_REQUIRED_FIELD: CaseDescriptor<RequiresAllFields> = CaseDescriptor {
1715 id: "error::missing_required_field",
1716 description: "missing required field error",
1717 expected: || RequiresAllFields {
1718 name: "Alice".into(),
1719 age: 30,
1720 email: "alice@example.com".into(),
1721 },
1722};
1723
1724const CASE_ATTR_ALIAS: CaseDescriptor<WithAlias> = CaseDescriptor {
1727 id: "attr::alias",
1728 description: "field with #[facet(alias = \"old_name\")] accepts alternative name",
1729 expected: || WithAlias {
1730 new_name: "value".into(),
1731 count: 5,
1732 },
1733};
1734
1735const CASE_ATTR_RENAME_VS_ALIAS: CaseDescriptor<RenameVsAlias> = CaseDescriptor {
1738 id: "attr::rename_vs_alias_precedence",
1739 description: "when both rename and alias present, rename takes precedence",
1740 expected: || RenameVsAlias {
1741 field: "test".into(),
1742 id: 1,
1743 },
1744};
1745
1746const CASE_ATTR_RENAME_ALL_KEBAB: CaseDescriptor<RenameAllKebab> = CaseDescriptor {
1747 id: "attr::rename_all_kebab",
1748 description: "struct with #[facet(rename_all = \"kebab-case\")]",
1749 expected: || RenameAllKebab {
1750 first_name: "John".into(),
1751 last_name: "Doe".into(),
1752 user_id: 42,
1753 },
1754};
1755
1756const CASE_ATTR_RENAME_ALL_SCREAMING: CaseDescriptor<RenameAllScreaming> = CaseDescriptor {
1757 id: "attr::rename_all_screaming",
1758 description: "struct with #[facet(rename_all = \"SCREAMING_SNAKE_CASE\")]",
1759 expected: || RenameAllScreaming {
1760 api_key: "secret-123".into(),
1761 max_retry_count: 5,
1762 },
1763};
1764
1765const CASE_ATTR_RENAME_UNICODE: CaseDescriptor<RenameUnicode> = CaseDescriptor {
1766 id: "attr::rename_unicode",
1767 description: "field with unicode (emoji) rename #[facet(rename = \"🎉\")]",
1768 expected: || RenameUnicode {
1769 celebration: "party".into(),
1770 },
1771};
1772
1773const CASE_ATTR_RENAME_SPECIAL_CHARS: CaseDescriptor<RenameSpecialChars> = CaseDescriptor {
1774 id: "attr::rename_special_chars",
1775 description: "field with special chars rename #[facet(rename = \"@type\")]",
1776 expected: || RenameSpecialChars {
1777 type_field: "node".into(),
1778 },
1779};
1780
1781const CASE_PROXY_CONTAINER: CaseDescriptor<ProxyInt> = CaseDescriptor {
1784 id: "proxy::container",
1785 description: "container-level #[facet(proxy = IntAsString)] deserializes int from string",
1786 expected: || ProxyInt { value: 42 },
1787};
1788
1789const CASE_PROXY_FIELD_LEVEL: CaseDescriptor<ProxyFieldLevel> = CaseDescriptor {
1790 id: "proxy::field_level",
1791 description: "field-level #[facet(proxy = IntAsString)] on individual field",
1792 expected: || ProxyFieldLevel {
1793 name: "test".into(),
1794 count: 100,
1795 },
1796};
1797
1798const CASE_PROXY_VALIDATION_ERROR: CaseDescriptor<ProxyInt> = CaseDescriptor {
1799 id: "proxy::validation_error",
1800 description: "proxy conversion error when validation fails (expects error)",
1801 expected: || ProxyInt { value: 0 }, };
1803
1804const CASE_PROXY_WITH_OPTION: CaseDescriptor<ProxyWithOption> = CaseDescriptor {
1805 id: "proxy::with_option",
1806 description: "proxy wrapping Option<T>",
1807 expected: || ProxyWithOption {
1808 name: "test".into(),
1809 count: Some(42),
1810 },
1811};
1812
1813const CASE_PROXY_WITH_ENUM: CaseDescriptor<ProxyEnum> = CaseDescriptor {
1814 id: "proxy::with_enum",
1815 description: "proxy on enum variant",
1816 expected: || ProxyEnum::Value(99),
1817};
1818
1819const CASE_PROXY_WITH_TRANSPARENT: CaseDescriptor<TransparentProxy> = CaseDescriptor {
1820 id: "proxy::with_transparent",
1821 description: "transparent wrapper with proxy",
1822 expected: || TransparentProxy(42),
1823};
1824
1825const CASE_OPAQUE_PROXY: CaseDescriptor<OpaqueProxyWrapper> = CaseDescriptor {
1826 id: "proxy::opaque",
1827 description: "#[facet(opaque, proxy = ...)] where target type doesn't implement Facet",
1828 expected: || OpaqueProxyWrapper {
1829 value: OpaqueType { inner: 42 },
1830 },
1831};
1832
1833const CASE_OPAQUE_PROXY_OPTION: CaseDescriptor<OpaqueProxyOptionWrapper> = CaseDescriptor {
1834 id: "proxy::opaque_option",
1835 description: "#[facet(opaque, proxy = ...)] on Option<OpaqueType>",
1836 expected: || OpaqueProxyOptionWrapper {
1837 value: Some(OpaqueType { inner: 99 }),
1838 },
1839};
1840
1841const CASE_TRANSPARENT_MULTILEVEL: CaseDescriptor<OuterTransparent> = CaseDescriptor {
1844 id: "transparent::multilevel",
1845 description: "transparent wrapping another transparent type",
1846 expected: || OuterTransparent(InnerTransparent(42)),
1847};
1848
1849const CASE_TRANSPARENT_OPTION: CaseDescriptor<TransparentOption> = CaseDescriptor {
1850 id: "transparent::option",
1851 description: "transparent wrapping Option<T>",
1852 expected: || TransparentOption(Some(99)),
1853};
1854
1855const CASE_TRANSPARENT_NONZERO: CaseDescriptor<TransparentNonZero> = CaseDescriptor {
1856 id: "transparent::nonzero",
1857 description: "transparent wrapping NonZero type",
1858 expected: || TransparentNonZero(std::num::NonZeroU32::new(42).unwrap()),
1859};
1860
1861const CASE_SCALAR_BOOL: CaseDescriptor<BoolWrapper> = CaseDescriptor {
1864 id: "scalar::bool",
1865 description: "boolean scalar values",
1866 expected: || BoolWrapper {
1867 yes: true,
1868 no: false,
1869 },
1870};
1871
1872const CASE_SCALAR_INTEGERS: CaseDescriptor<IntegerTypes> = CaseDescriptor {
1873 id: "scalar::integers",
1874 description: "various integer types (i8, u8, i32, u32, i64, u64)",
1875 expected: || IntegerTypes {
1876 signed_8: -128,
1877 unsigned_8: 255,
1878 signed_32: -2_147_483_648,
1879 unsigned_32: 4_294_967_295,
1880 signed_64: -9_223_372_036_854_775_808,
1881 unsigned_64: 18_446_744_073_709_551_615,
1882 },
1883};
1884
1885const CASE_SCALAR_FLOATS: CaseDescriptor<FloatTypes> = CaseDescriptor {
1886 id: "scalar::floats",
1887 description: "floating point types (f32, f64)",
1888 expected: || FloatTypes {
1889 float_32: 1.5,
1890 float_64: 2.25,
1891 },
1892};
1893
1894const CASE_SCALAR_FLOATS_SCIENTIFIC: CaseDescriptor<FloatTypesScientific> = CaseDescriptor {
1895 id: "scalar::floats_scientific",
1896 description: "floating point with scientific notation (1.23e10, -4.56e-7)",
1897 expected: || FloatTypesScientific {
1898 large: 1.23e10,
1899 small: -4.56e-7,
1900 positive_exp: 5e3,
1901 },
1902};
1903
1904#[cfg(feature = "net")]
1907const CASE_NET_IP_ADDR_V4: CaseDescriptor<IpAddrV4Wrapper> = CaseDescriptor {
1908 id: "net::ip_addr_v4",
1909 description: "std::net::IpAddr (IPv4 variant)",
1910 expected: || IpAddrV4Wrapper {
1911 addr: std::net::IpAddr::V4(std::net::Ipv4Addr::new(192, 168, 1, 1)),
1912 },
1913};
1914
1915#[cfg(feature = "net")]
1916const CASE_NET_IP_ADDR_V6: CaseDescriptor<IpAddrV6Wrapper> = CaseDescriptor {
1917 id: "net::ip_addr_v6",
1918 description: "std::net::IpAddr (IPv6 variant)",
1919 expected: || IpAddrV6Wrapper {
1920 addr: std::net::IpAddr::V6(std::net::Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)),
1921 },
1922};
1923
1924#[cfg(feature = "net")]
1925const CASE_NET_IPV4_ADDR: CaseDescriptor<Ipv4AddrWrapper> = CaseDescriptor {
1926 id: "net::ipv4_addr",
1927 description: "std::net::Ipv4Addr",
1928 expected: || Ipv4AddrWrapper {
1929 addr: std::net::Ipv4Addr::new(127, 0, 0, 1),
1930 },
1931};
1932
1933#[cfg(feature = "net")]
1934const CASE_NET_IPV6_ADDR: CaseDescriptor<Ipv6AddrWrapper> = CaseDescriptor {
1935 id: "net::ipv6_addr",
1936 description: "std::net::Ipv6Addr",
1937 expected: || Ipv6AddrWrapper {
1938 addr: std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1),
1939 },
1940};
1941
1942#[cfg(feature = "net")]
1943const CASE_NET_SOCKET_ADDR_V4: CaseDescriptor<SocketAddrV4Wrapper> = CaseDescriptor {
1944 id: "net::socket_addr_v4",
1945 description: "std::net::SocketAddr (IPv4 variant)",
1946 expected: || SocketAddrV4Wrapper {
1947 addr: std::net::SocketAddr::new(
1948 std::net::IpAddr::V4(std::net::Ipv4Addr::new(192, 168, 1, 1)),
1949 8080,
1950 ),
1951 },
1952};
1953
1954#[cfg(feature = "net")]
1955const CASE_NET_SOCKET_ADDR_V6: CaseDescriptor<SocketAddrV6Wrapper> = CaseDescriptor {
1956 id: "net::socket_addr_v6",
1957 description: "std::net::SocketAddr (IPv6 variant)",
1958 expected: || SocketAddrV6Wrapper {
1959 addr: std::net::SocketAddr::new(
1960 std::net::IpAddr::V6(std::net::Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)),
1961 443,
1962 ),
1963 },
1964};
1965
1966#[cfg(feature = "net")]
1967const CASE_NET_SOCKET_ADDR_V4_EXPLICIT: CaseDescriptor<SocketAddrV4ExplicitWrapper> =
1968 CaseDescriptor {
1969 id: "net::socket_addr_v4_explicit",
1970 description: "std::net::SocketAddrV4 (explicit type)",
1971 expected: || SocketAddrV4ExplicitWrapper {
1972 addr: std::net::SocketAddrV4::new(std::net::Ipv4Addr::new(10, 0, 0, 1), 3000),
1973 },
1974 };
1975
1976#[cfg(feature = "net")]
1977const CASE_NET_SOCKET_ADDR_V6_EXPLICIT: CaseDescriptor<SocketAddrV6ExplicitWrapper> =
1978 CaseDescriptor {
1979 id: "net::socket_addr_v6_explicit",
1980 description: "std::net::SocketAddrV6 (explicit type)",
1981 expected: || SocketAddrV6ExplicitWrapper {
1982 addr: std::net::SocketAddrV6::new(
1983 std::net::Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
1984 9000,
1985 0,
1986 0,
1987 ),
1988 },
1989 };
1990
1991const CASE_MAP_STRING_KEYS: CaseDescriptor<MapWrapper> = CaseDescriptor {
1994 id: "collection::map",
1995 description: "BTreeMap<String, i32> with string keys",
1996 expected: || {
1997 let mut map = std::collections::BTreeMap::new();
1998 map.insert("alpha".into(), 1);
1999 map.insert("beta".into(), 2);
2000 MapWrapper { data: map }
2001 },
2002};
2003
2004const CASE_TUPLE_SIMPLE: CaseDescriptor<TupleWrapper> = CaseDescriptor {
2005 id: "collection::tuple",
2006 description: "tuple (String, i32, bool)",
2007 expected: || TupleWrapper {
2008 triple: ("hello".into(), 42, true),
2009 },
2010};
2011
2012const CASE_TUPLE_NESTED: CaseDescriptor<NestedTupleWrapper> = CaseDescriptor {
2013 id: "collection::tuple_nested",
2014 description: "nested tuple ((i32, i32), (String, bool))",
2015 expected: || NestedTupleWrapper {
2016 outer: ((1, 2), ("test".into(), true)),
2017 },
2018};
2019
2020const CASE_TUPLE_EMPTY: CaseDescriptor<EmptyTupleWrapper> = CaseDescriptor {
2021 id: "collection::tuple_empty",
2022 description: "empty tuple () as a field",
2023 expected: || EmptyTupleWrapper {
2024 name: "test".into(),
2025 empty: (),
2026 },
2027};
2028
2029const CASE_TUPLE_SINGLE_ELEMENT: CaseDescriptor<SingleElementTupleWrapper> = CaseDescriptor {
2030 id: "collection::tuple_single_element",
2031 description: "single-element tuple (T,) as a field",
2032 expected: || SingleElementTupleWrapper {
2033 name: "test".into(),
2034 single: (42,),
2035 },
2036};
2037
2038const CASE_TUPLE_STRUCT_VARIANT: CaseDescriptor<TupleVariantEnum> = CaseDescriptor {
2039 id: "collection::tuple_struct_variant",
2040 description: "enum with tuple variant Variant(T, U)",
2041 expected: || TupleVariantEnum::Pair("test".into(), 42),
2042};
2043
2044const CASE_TUPLE_NEWTYPE_VARIANT: CaseDescriptor<NewtypeVariantEnum> = CaseDescriptor {
2045 id: "collection::tuple_newtype_variant",
2046 description: "enum with newtype variant Variant(T)",
2047 expected: || NewtypeVariantEnum::Some(99),
2048};
2049
2050const CASE_ENUM_UNIT_VARIANT: CaseDescriptor<UnitVariantEnum> = CaseDescriptor {
2053 id: "enum::unit_variant",
2054 description: "enum with unit variants",
2055 expected: || UnitVariantEnum::Active,
2056};
2057
2058const CASE_NUMERIC_ENUM: CaseDescriptor<NumericEnum> = CaseDescriptor {
2059 id: "enum::numeric",
2060 description: "#[facet(is_numeric)] enum matches by structure",
2061 expected: || NumericEnum::B,
2062};
2063
2064const CASE_ENUM_UNTAGGED: CaseDescriptor<UntaggedEnum> = CaseDescriptor {
2065 id: "enum::untagged",
2066 description: "#[facet(untagged)] enum matches by structure",
2067 expected: || UntaggedEnum::Point { x: 10, y: 20 },
2068};
2069
2070const CASE_ENUM_VARIANT_RENAME: CaseDescriptor<EnumVariantRename> = CaseDescriptor {
2071 id: "enum::variant_rename",
2072 description: "#[facet(rename = \"...\")] on enum variants",
2073 expected: || EnumVariantRename::Active,
2074};
2075
2076const CASE_SIGNED_NUMERIC_ENUM: CaseDescriptor<SignedNumericEnum> = CaseDescriptor {
2079 id: "enum::numeric::signed",
2080 description: "Numeric enum with signed discriminant",
2081 expected: || SignedNumericEnum::Z,
2082};
2083
2084const CASE_INFERRED_NUMERIC_ENUM: CaseDescriptor<NumericEnum> = CaseDescriptor {
2085 id: "enum::numeric::inferred",
2086 description: "Numeric enum that is inferred from string",
2087 expected: || NumericEnum::A,
2088};
2089
2090const CASE_UNTAGGED_WITH_NULL: CaseDescriptor<UntaggedWithNull> = CaseDescriptor {
2093 id: "untagged::with_null",
2094 description: "untagged enum with unit variant matching null",
2095 expected: || UntaggedWithNull::None,
2096};
2097
2098const CASE_UNTAGGED_NEWTYPE_VARIANT: CaseDescriptor<UntaggedNewtype> = CaseDescriptor {
2099 id: "untagged::newtype_variant",
2100 description: "untagged enum with newtype variants (discrimination)",
2101 expected: || UntaggedNewtype::String("test".into()),
2102};
2103
2104const CASE_UNTAGGED_AS_FIELD: CaseDescriptor<UntaggedAsField> = CaseDescriptor {
2105 id: "untagged::as_field",
2106 description: "untagged enum as struct field (nesting)",
2107 expected: || UntaggedAsField {
2108 name: "test".into(),
2109 value: UntaggedNewtype::Number(42),
2110 },
2111};
2112
2113const CASE_UNTAGGED_UNIT_ONLY: CaseDescriptor<UntaggedUnitOnly> = CaseDescriptor {
2114 id: "untagged::unit_only",
2115 description: "untagged enum with only unit variants (dataless)",
2116 expected: || UntaggedUnitOnly::Alpha,
2117};
2118
2119const CASE_BOX_WRAPPER: CaseDescriptor<BoxWrapper> = CaseDescriptor {
2122 id: "pointer::box",
2123 description: "Box<T> smart pointer",
2124 expected: || BoxWrapper {
2125 inner: Box::new(42),
2126 },
2127};
2128
2129const CASE_ARC_WRAPPER: CaseDescriptor<ArcWrapper> = CaseDescriptor {
2130 id: "pointer::arc",
2131 description: "Arc<T> smart pointer",
2132 expected: || ArcWrapper {
2133 inner: std::sync::Arc::new(42),
2134 },
2135};
2136
2137const CASE_RC_WRAPPER: CaseDescriptor<RcWrapper> = CaseDescriptor {
2138 id: "pointer::rc",
2139 description: "Rc<T> smart pointer",
2140 expected: || RcWrapper {
2141 inner: std::rc::Rc::new(42),
2142 },
2143};
2144
2145const CASE_BOX_STR: CaseDescriptor<BoxStrWrapper> = CaseDescriptor {
2146 id: "pointer::box_str",
2147 description: "Box<str> unsized smart pointer",
2148 expected: || BoxStrWrapper {
2149 inner: Box::from("hello world"),
2150 },
2151};
2152
2153const CASE_ARC_STR: CaseDescriptor<ArcStrWrapper> = CaseDescriptor {
2154 id: "pointer::arc_str",
2155 description: "Arc<str> unsized smart pointer",
2156 expected: || ArcStrWrapper {
2157 inner: std::sync::Arc::from("hello world"),
2158 },
2159};
2160
2161const CASE_RC_STR: CaseDescriptor<RcStrWrapper> = CaseDescriptor {
2162 id: "pointer::rc_str",
2163 description: "Rc<str> unsized smart pointer",
2164 expected: || RcStrWrapper {
2165 inner: std::rc::Rc::from("hello world"),
2166 },
2167};
2168
2169const CASE_ARC_SLICE: CaseDescriptor<ArcSliceWrapper> = CaseDescriptor {
2170 id: "pointer::arc_slice",
2171 description: "Arc<[T]> unsized slice smart pointer",
2172 expected: || ArcSliceWrapper {
2173 inner: std::sync::Arc::from([1i32, 2, 3, 4]),
2174 },
2175};
2176
2177#[cfg(feature = "yoke")]
2180const CASE_YOKE_COW_STR: CaseDescriptor<YokeWrapper> = CaseDescriptor {
2181 id: "pointer::yoke_cow_str",
2182 description: "Yoke<Cow<'static, str>, Arc<str>> zero-copy smart pointer",
2183 expected: || YokeWrapper {
2184 value: yoke::Yoke::<std::borrow::Cow<'static, str>, std::sync::Arc<str>>::attach_to_cart(
2185 std::sync::Arc::from("hello yoke"),
2186 |s| std::borrow::Cow::Borrowed(s),
2187 ),
2188 },
2189};
2190
2191#[cfg(feature = "yoke")]
2192const CASE_YOKE_CUSTOM: CaseDescriptor<YokingWrapper> = CaseDescriptor {
2193 id: "pointer::yoke_custom",
2194 description: "Custom type deriving Yokeable and using #[facet(try_from_ref)]",
2195 expected: || YokingWrapper {
2196 value: yoke::Yoke::<SplittingYoker<'static>, std::sync::Arc<str>>::attach_to_cart(
2197 std::sync::Arc::from("hello|yoke"),
2198 |s| SplittingYoker::try_from_ref(s).unwrap(),
2199 ),
2200 },
2201};
2202
2203const CASE_SET_BTREE: CaseDescriptor<SetWrapper> = CaseDescriptor {
2206 id: "collection::set",
2207 description: "BTreeSet<String>",
2208 expected: || {
2209 let mut set = std::collections::BTreeSet::new();
2210 set.insert("alpha".into());
2211 set.insert("beta".into());
2212 set.insert("gamma".into());
2213 SetWrapper { items: set }
2214 },
2215};
2216
2217const CASE_SCALAR_INTEGERS_16: CaseDescriptor<IntegerTypes16> = CaseDescriptor {
2220 id: "scalar::integers_16",
2221 description: "16-bit integer types (i16, u16)",
2222 expected: || IntegerTypes16 {
2223 signed_16: -32768,
2224 unsigned_16: 65535,
2225 },
2226};
2227
2228const CASE_SCALAR_INTEGERS_128: CaseDescriptor<IntegerTypes128> = CaseDescriptor {
2229 id: "scalar::integers_128",
2230 description: "128-bit integer types (i128, u128)",
2231 expected: || IntegerTypes128 {
2232 signed_128: -170_141_183_460_469_231_731_687_303_715_884_105_728,
2233 unsigned_128: 340_282_366_920_938_463_463_374_607_431_768_211_455,
2234 },
2235};
2236
2237const CASE_SCALAR_INTEGERS_SIZE: CaseDescriptor<IntegerTypesSize> = CaseDescriptor {
2238 id: "scalar::integers_size",
2239 description: "pointer-sized integer types (isize, usize)",
2240 expected: || IntegerTypesSize {
2241 signed_size: -1000,
2242 unsigned_size: 2000,
2243 },
2244};
2245
2246const CASE_NONZERO_INTEGERS: CaseDescriptor<NonZeroTypes> = CaseDescriptor {
2249 id: "scalar::nonzero",
2250 description: "NonZero integer types",
2251 expected: || NonZeroTypes {
2252 nz_u32: std::num::NonZeroU32::new(42).unwrap(),
2253 nz_i64: std::num::NonZeroI64::new(-100).unwrap(),
2254 },
2255};
2256
2257const CASE_NONZERO_INTEGERS_EXTENDED: CaseDescriptor<NonZeroTypesExtended> = CaseDescriptor {
2258 id: "scalar::nonzero_extended",
2259 description: "Extended NonZero integer types (8, 16, 128, size)",
2260 expected: || NonZeroTypesExtended {
2261 nz_u8: std::num::NonZeroU8::new(255).unwrap(),
2262 nz_i8: std::num::NonZeroI8::new(-128).unwrap(),
2263 nz_u16: std::num::NonZeroU16::new(65535).unwrap(),
2264 nz_i16: std::num::NonZeroI16::new(-32768).unwrap(),
2265 nz_u128: std::num::NonZeroU128::new(1).unwrap(),
2266 nz_i128: std::num::NonZeroI128::new(-1).unwrap(),
2267 nz_usize: std::num::NonZeroUsize::new(1000).unwrap(),
2268 nz_isize: std::num::NonZeroIsize::new(-500).unwrap(),
2269 },
2270};
2271
2272const CASE_COW_STR: CaseDescriptor<CowStrWrapper> = CaseDescriptor {
2275 id: "string::cow_str",
2276 description: "Cow<'static, str> string fields",
2277 expected: || CowStrWrapper {
2278 owned: std::borrow::Cow::Owned("hello world".to_string()),
2279 message: std::borrow::Cow::Borrowed("borrowed"),
2280 },
2281};
2282
2283const CASE_NEWTYPE_U64: CaseDescriptor<NewtypeU64Wrapper> = CaseDescriptor {
2286 id: "newtype::u64",
2287 description: "newtype wrapper around u64",
2288 expected: || NewtypeU64Wrapper {
2289 value: NewtypeU64(42),
2290 },
2291};
2292
2293const CASE_NEWTYPE_STRING: CaseDescriptor<NewtypeStringWrapper> = CaseDescriptor {
2294 id: "newtype::string",
2295 description: "newtype wrapper around String",
2296 expected: || NewtypeStringWrapper {
2297 value: NewtypeString("hello".into()),
2298 },
2299};
2300
2301const CASE_CHAR_SCALAR: CaseDescriptor<CharWrapper> = CaseDescriptor {
2304 id: "scalar::char",
2305 description: "char scalar type",
2306 expected: || CharWrapper {
2307 letter: 'A',
2308 emoji: '🦀',
2309 },
2310};
2311
2312const CASE_HASHSET: CaseDescriptor<HashSetWrapper> = CaseDescriptor {
2315 id: "collection::hashset",
2316 description: "HashSet<String>",
2317 expected: || {
2318 let mut set = std::collections::HashSet::new();
2319 set.insert("alpha".into());
2320 set.insert("beta".into());
2321 HashSetWrapper { items: set }
2322 },
2323};
2324
2325const CASE_VEC_NESTED: CaseDescriptor<NestedVecWrapper> = CaseDescriptor {
2328 id: "collection::vec_nested",
2329 description: "nested Vec<Vec<i32>>",
2330 expected: || NestedVecWrapper {
2331 matrix: vec![vec![1, 2], vec![3, 4, 5]],
2332 },
2333};
2334
2335const CASE_BYTES_VEC_U8: CaseDescriptor<BytesWrapper> = CaseDescriptor {
2338 id: "slice::bytes::vec_u8",
2339 description: "Vec<u8> binary data as array of numbers",
2340 expected: || BytesWrapper {
2341 data: vec![0, 128, 255, 42],
2342 },
2343};
2344
2345const CASE_ARRAY_FIXED_SIZE: CaseDescriptor<ArrayWrapper> = CaseDescriptor {
2348 id: "array::fixed_size",
2349 description: "[T; N] fixed-size array",
2350 expected: || ArrayWrapper { values: [1, 2, 3] },
2351};
2352
2353const CASE_SKIP_UNKNOWN_FIELDS: CaseDescriptor<SkipUnknownStruct> = CaseDescriptor {
2356 id: "behavior::skip_unknown_fields",
2357 description: "unknown fields are silently skipped by default",
2358 expected: || SkipUnknownStruct {
2359 known: "value".into(),
2360 },
2361};
2362
2363const CASE_STRING_ESCAPES: CaseDescriptor<StringEscapes> = CaseDescriptor {
2366 id: "string::escapes",
2367 description: "string with escape sequences (\\n, \\t, \\\", \\\\)",
2368 expected: || StringEscapes {
2369 text: "line1\nline2\ttab\"quote\\backslash".into(),
2370 },
2371};
2372
2373const CASE_STRING_ESCAPES_EXTENDED: CaseDescriptor<StringEscapesExtended> = CaseDescriptor {
2374 id: "string::escapes_extended",
2375 description: "string with extended escape sequences (\\b, \\f, \\r, \\u0001)",
2376 expected: || StringEscapesExtended {
2377 backspace: "hello\x08world".into(),
2378 formfeed: "page\x0Cbreak".into(),
2379 carriage_return: "line\rreturn".into(),
2380 control_char: "\x01".into(),
2381 },
2382};
2383
2384const CASE_UNIT_STRUCT: CaseDescriptor<UnitStruct> = CaseDescriptor {
2387 id: "unit::struct",
2388 description: "unit struct (zero-sized type)",
2389 expected: || UnitStruct,
2390};
2391
2392#[derive(Facet, Debug, Clone, PartialEq)]
2394#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
2395pub struct StructSingleField {
2396 pub name: String,
2397}
2398
2399#[derive(Facet, Debug, Clone, PartialEq)]
2401#[facet(untagged)]
2402#[repr(u8)]
2403pub enum MixedScalar {
2404 Signed(i64),
2405 Float(f64),
2406 Bool(bool),
2407 Null,
2408}
2409
2410#[derive(Facet, Debug, Clone, PartialEq)]
2411#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
2412pub struct NestedParent {
2413 pub id: u64,
2414 pub child: NestedChild,
2415 pub tags: Vec<String>,
2416}
2417
2418#[derive(Facet, Debug, Clone, PartialEq)]
2419#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
2420pub struct NestedChild {
2421 pub code: String,
2422 pub active: bool,
2423}
2424
2425#[derive(Facet, Debug, Clone, PartialEq)]
2426#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
2427#[repr(u8)]
2428pub enum ComplexEnum {
2429 Empty,
2430 Count(u64),
2431 Label { name: String, level: u8 },
2432}
2433
2434#[derive(Facet, Debug, Clone, PartialEq)]
2438pub struct RenamedField {
2439 #[facet(rename = "userName")]
2440 pub user_name: String,
2441 pub age: u32,
2442}
2443
2444#[derive(Facet, Debug, Clone, PartialEq)]
2446#[facet(rename_all = "camelCase")]
2447pub struct CamelCaseStruct {
2448 pub first_name: String,
2449 pub last_name: String,
2450 pub is_active: bool,
2451}
2452
2453#[derive(Facet, Debug, Clone, PartialEq)]
2455pub struct WithDefault {
2456 pub required: String,
2457 #[facet(default)]
2458 pub optional_count: u32,
2459}
2460
2461#[derive(Facet, Default, Debug, Clone, PartialEq)]
2463#[facet(default)]
2464pub struct StructLevelDefault {
2465 pub count: i32,
2466 pub message: String,
2467}
2468
2469pub const fn custom_default_value() -> i32 {
2471 42
2472}
2473
2474#[derive(Facet, Debug, Clone, PartialEq)]
2476pub struct WithDefaultFunction {
2477 #[facet(default = custom_default_value())]
2478 pub magic_number: i32,
2479 pub name: String,
2480}
2481
2482#[derive(Facet, Debug, Clone, PartialEq)]
2484#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
2485pub struct WithOption {
2486 pub name: String,
2487 pub nickname: Option<String>,
2488}
2489
2490#[derive(Facet, Debug, Clone, PartialEq)]
2492pub struct WithSkipSerializing {
2493 pub visible: String,
2494 #[facet(skip_serializing)]
2495 #[facet(default)]
2496 pub hidden: String,
2497}
2498
2499#[derive(Facet, Debug, Clone, PartialEq)]
2501pub struct WithSkipSerializingIf {
2502 pub name: String,
2503 #[facet(skip_serializing_if = Option::is_none)]
2504 pub optional_data: Option<String>,
2505}
2506
2507#[derive(Facet, Debug, Clone, PartialEq)]
2509pub struct WithSkip {
2510 pub visible: String,
2511 #[facet(skip)]
2512 #[facet(default)]
2513 pub internal: u32,
2514}
2515
2516#[derive(Facet, Debug, Clone, PartialEq)]
2520#[facet(tag = "type")]
2521#[repr(u8)]
2522pub enum InternallyTagged {
2523 Circle { radius: f64 },
2524 Rectangle { width: f64, height: f64 },
2525}
2526
2527#[derive(Facet, Debug, Clone, PartialEq)]
2529#[facet(tag = "t", content = "c")]
2530#[repr(u8)]
2531pub enum AdjacentlyTagged {
2532 Message(String),
2533 Count(u64),
2534}
2535
2536#[derive(Facet, Debug, Clone, PartialEq)]
2540pub struct FlattenInner {
2541 pub x: i32,
2542 pub y: i32,
2543}
2544
2545#[derive(Facet, Debug, Clone, PartialEq)]
2547pub struct FlattenOuter {
2548 pub name: String,
2549 #[facet(flatten)]
2550 pub coords: FlattenInner,
2551}
2552
2553#[derive(Facet, Debug, Clone, PartialEq)]
2555#[facet(transparent)]
2556pub struct UserId(pub u64);
2557
2558#[derive(Facet, Debug, Clone, PartialEq)]
2560pub struct UserRecord {
2561 pub id: UserId,
2562 pub name: String,
2563}
2564
2565#[derive(Facet, Debug, Clone, PartialEq)]
2569pub struct FlattenOptionalSome {
2570 pub name: String,
2571 #[facet(flatten)]
2572 pub metadata: Option<FlattenMetadata>,
2573}
2574
2575#[derive(Facet, Debug, Clone, PartialEq)]
2577pub struct FlattenOptionalNone {
2578 pub name: String,
2579 #[facet(flatten)]
2580 pub metadata: Option<FlattenMetadata>,
2581}
2582
2583#[derive(Facet, Debug, Clone, PartialEq)]
2585pub struct FlattenMetadata {
2586 pub version: i32,
2587 pub author: String,
2588}
2589
2590#[derive(Facet, Debug, Clone, PartialEq)]
2592pub struct FlattenPartA {
2593 pub field_a: String,
2594 pub shared: i32,
2595}
2596
2597#[derive(Facet, Debug, Clone, PartialEq)]
2599pub struct FlattenPartB {
2600 pub field_b: String,
2601 pub shared: i32,
2602}
2603
2604#[derive(Facet, Debug, Clone, PartialEq)]
2606pub struct FlattenOverlapping {
2607 #[facet(flatten)]
2608 pub part_a: FlattenPartA,
2609 #[facet(flatten)]
2610 pub part_b: FlattenPartB,
2611}
2612
2613#[derive(Facet, Debug, Clone, PartialEq)]
2615pub struct FlattenLevel3 {
2616 pub deep_field: i32,
2617}
2618
2619#[derive(Facet, Debug, Clone, PartialEq)]
2621pub struct FlattenLevel2 {
2622 pub mid_field: i32,
2623 #[facet(flatten)]
2624 pub level3: FlattenLevel3,
2625}
2626
2627#[derive(Facet, Debug, Clone, PartialEq)]
2629pub struct FlattenLevel1 {
2630 pub top_field: String,
2631 #[facet(flatten)]
2632 pub level2: FlattenLevel2,
2633}
2634
2635#[derive(Facet, Debug, Clone, PartialEq)]
2637pub struct FlattenAuthPassword {
2638 pub password: String,
2639}
2640
2641#[derive(Facet, Debug, Clone, PartialEq)]
2643pub struct FlattenAuthToken {
2644 pub token: String,
2645}
2646
2647#[allow(dead_code)]
2649#[derive(Facet, Debug, Clone, PartialEq)]
2650#[repr(u8)]
2651pub enum FlattenAuthMethod {
2652 Password(FlattenAuthPassword),
2653 Token(FlattenAuthToken),
2654}
2655
2656#[derive(Facet, Debug, Clone, PartialEq)]
2658pub struct FlattenTransportTcp {
2659 pub port: u16,
2660}
2661
2662#[derive(Facet, Debug, Clone, PartialEq)]
2664pub struct FlattenTransportUnix {
2665 pub socket: String,
2666}
2667
2668#[allow(dead_code)]
2670#[derive(Facet, Debug, Clone, PartialEq)]
2671#[repr(u8)]
2672pub enum FlattenTransport {
2673 Tcp(FlattenTransportTcp),
2674 Unix(FlattenTransportUnix),
2675}
2676
2677#[derive(Facet, Debug, Clone, PartialEq)]
2679pub struct FlattenMultipleEnums {
2680 pub name: String,
2681 #[facet(flatten)]
2682 pub auth: FlattenAuthMethod,
2683 #[facet(flatten)]
2684 pub transport: FlattenTransport,
2685}
2686
2687#[derive(Facet, Debug, Clone, PartialEq)]
2691#[facet(deny_unknown_fields)]
2692pub struct DenyUnknownStruct {
2693 pub foo: String,
2694 pub bar: i32,
2695}
2696
2697#[derive(Facet, Debug, Clone, PartialEq)]
2699pub struct ExpectsInteger {
2700 pub value: i32,
2701}
2702
2703#[derive(Facet, Debug, Clone, PartialEq)]
2705pub struct ExpectsArray {
2706 pub items: Vec<i32>,
2707}
2708
2709#[derive(Facet, Debug, Clone, PartialEq)]
2711pub struct RequiresAllFields {
2712 pub name: String,
2713 pub age: u32,
2714 pub email: String,
2715}
2716
2717#[derive(Facet, Debug, Clone, PartialEq)]
2719pub struct WithAlias {
2720 #[facet(alias = "old_name")]
2721 pub new_name: String,
2722 pub count: u32,
2723}
2724
2725#[derive(Facet, Debug, Clone, PartialEq)]
2729pub struct RenameVsAlias {
2730 #[facet(rename = "officialName")]
2731 #[facet(alias = "oldName")]
2732 pub field: String,
2733 pub id: u32,
2734}
2735
2736#[derive(Facet, Debug, Clone, PartialEq)]
2738#[facet(rename_all = "kebab-case")]
2739pub struct RenameAllKebab {
2740 pub first_name: String,
2741 pub last_name: String,
2742 pub user_id: u32,
2743}
2744
2745#[derive(Facet, Debug, Clone, PartialEq)]
2747#[facet(rename_all = "SCREAMING_SNAKE_CASE")]
2748pub struct RenameAllScreaming {
2749 pub api_key: String,
2750 pub max_retry_count: u32,
2751}
2752
2753#[derive(Facet, Clone, Debug, PartialEq)]
2755pub struct RenameUnicode {
2756 #[facet(rename = "🎉")]
2757 pub celebration: String,
2758}
2759
2760#[derive(Facet, Clone, Debug, PartialEq)]
2762pub struct RenameSpecialChars {
2763 #[facet(rename = "@type")]
2764 pub type_field: String,
2765}
2766
2767#[derive(Facet, Clone, Debug)]
2771#[facet(transparent)]
2772pub struct IntAsString(pub String);
2773
2774#[derive(Facet, Debug, Clone, PartialEq)]
2776#[facet(proxy = IntAsString)]
2777pub struct ProxyInt {
2778 pub value: i32,
2779}
2780
2781impl TryFrom<IntAsString> for ProxyInt {
2783 type Error = std::num::ParseIntError;
2784 fn try_from(proxy: IntAsString) -> Result<Self, Self::Error> {
2785 Ok(ProxyInt {
2786 value: proxy.0.parse()?,
2787 })
2788 }
2789}
2790
2791impl From<&ProxyInt> for IntAsString {
2793 fn from(v: &ProxyInt) -> Self {
2794 IntAsString(v.value.to_string())
2795 }
2796}
2797
2798#[derive(Facet, Debug, Clone, PartialEq)]
2800pub struct ProxyFieldLevel {
2801 pub name: String,
2802 #[facet(proxy = IntAsString)]
2803 pub count: i32,
2804}
2805
2806impl TryFrom<IntAsString> for i32 {
2808 type Error = std::num::ParseIntError;
2809 fn try_from(proxy: IntAsString) -> Result<Self, Self::Error> {
2810 proxy.0.parse()
2811 }
2812}
2813
2814impl From<&i32> for IntAsString {
2816 fn from(value: &i32) -> Self {
2817 IntAsString(value.to_string())
2818 }
2819}
2820
2821#[derive(Facet, Debug, Clone, PartialEq)]
2823pub struct ProxyWithOption {
2824 pub name: String,
2825 #[facet(proxy = IntAsString)]
2826 pub count: Option<i32>,
2827}
2828
2829impl TryFrom<IntAsString> for Option<i32> {
2831 type Error = std::num::ParseIntError;
2832 fn try_from(proxy: IntAsString) -> Result<Self, Self::Error> {
2833 if proxy.0.is_empty() {
2834 Ok(None)
2835 } else {
2836 Ok(Some(proxy.0.parse()?))
2837 }
2838 }
2839}
2840
2841impl From<&Option<i32>> for IntAsString {
2843 fn from(value: &Option<i32>) -> Self {
2844 match value {
2845 Some(v) => IntAsString(v.to_string()),
2846 None => IntAsString(String::new()),
2847 }
2848 }
2849}
2850
2851#[derive(Facet, Debug, Clone, PartialEq)]
2853#[repr(u8)]
2854pub enum ProxyEnum {
2855 None,
2856 #[facet(proxy = IntAsString)]
2857 Value(i32),
2858}
2859
2860#[derive(Facet, Debug, Clone, PartialEq)]
2862#[facet(transparent, proxy = IntAsString)]
2863pub struct TransparentProxy(pub i32);
2864
2865impl TryFrom<IntAsString> for TransparentProxy {
2867 type Error = std::num::ParseIntError;
2868 fn try_from(proxy: IntAsString) -> Result<Self, Self::Error> {
2869 Ok(TransparentProxy(proxy.0.parse()?))
2870 }
2871}
2872
2873impl From<&TransparentProxy> for IntAsString {
2875 fn from(value: &TransparentProxy) -> Self {
2876 IntAsString(value.0.to_string())
2877 }
2878}
2879
2880#[derive(Debug, Clone, PartialEq)]
2885pub struct OpaqueType {
2886 pub inner: u64,
2887}
2888
2889#[derive(Facet, Clone, Debug)]
2891pub struct OpaqueTypeProxy {
2892 pub inner: u64,
2893}
2894
2895impl From<OpaqueTypeProxy> for OpaqueType {
2897 fn from(proxy: OpaqueTypeProxy) -> Self {
2898 OpaqueType { inner: proxy.inner }
2899 }
2900}
2901
2902impl From<&OpaqueType> for OpaqueTypeProxy {
2904 fn from(v: &OpaqueType) -> Self {
2905 OpaqueTypeProxy { inner: v.inner }
2906 }
2907}
2908
2909#[derive(Facet, Debug, Clone, PartialEq)]
2911pub struct OpaqueProxyWrapper {
2912 #[facet(opaque, proxy = OpaqueTypeProxy)]
2913 pub value: OpaqueType,
2914}
2915
2916impl From<OpaqueTypeProxy> for Option<OpaqueType> {
2918 fn from(proxy: OpaqueTypeProxy) -> Self {
2919 Some(OpaqueType { inner: proxy.inner })
2920 }
2921}
2922
2923impl From<&Option<OpaqueType>> for OpaqueTypeProxy {
2925 fn from(v: &Option<OpaqueType>) -> Self {
2926 match v {
2927 Some(ot) => OpaqueTypeProxy { inner: ot.inner },
2928 None => OpaqueTypeProxy { inner: 0 },
2929 }
2930 }
2931}
2932
2933#[derive(Facet, Debug, Clone, PartialEq)]
2935pub struct OpaqueProxyOptionWrapper {
2936 #[facet(opaque, proxy = OpaqueTypeProxy)]
2937 pub value: Option<OpaqueType>,
2938}
2939
2940#[derive(Facet, Debug, Clone, PartialEq)]
2944#[facet(transparent)]
2945pub struct InnerTransparent(pub i32);
2946
2947#[derive(Facet, Debug, Clone, PartialEq)]
2949#[facet(transparent)]
2950pub struct OuterTransparent(pub InnerTransparent);
2951
2952#[derive(Facet, Debug, Clone, PartialEq)]
2954#[facet(transparent)]
2955pub struct TransparentOption(pub Option<i32>);
2956
2957#[derive(Facet, Debug, Clone, PartialEq)]
2959#[facet(transparent)]
2960pub struct TransparentNonZero(pub std::num::NonZeroU32);
2961
2962#[derive(Facet, Debug, Clone, PartialEq)]
2966#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
2967pub struct BoolWrapper {
2968 pub yes: bool,
2969 pub no: bool,
2970}
2971
2972#[derive(Facet, Debug, Clone, PartialEq)]
2974#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
2975pub struct IntegerTypes {
2976 pub signed_8: i8,
2977 pub unsigned_8: u8,
2978 pub signed_32: i32,
2979 pub unsigned_32: u32,
2980 pub signed_64: i64,
2981 pub unsigned_64: u64,
2982}
2983
2984#[derive(Facet, Debug, Clone, PartialEq)]
2986#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
2987pub struct FloatTypes {
2988 pub float_32: f32,
2989 pub float_64: f64,
2990}
2991
2992#[derive(Facet, Debug, Clone, PartialEq)]
2994#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
2995pub struct FloatTypesScientific {
2996 pub large: f64,
2997 pub small: f64,
2998 pub positive_exp: f64,
2999}
3000
3001#[cfg(feature = "net")]
3005#[derive(Facet, Debug, Clone, PartialEq)]
3006#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3007pub struct IpAddrV4Wrapper {
3008 pub addr: std::net::IpAddr,
3009}
3010
3011#[cfg(feature = "net")]
3013#[derive(Facet, Debug, Clone, PartialEq)]
3014#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3015pub struct IpAddrV6Wrapper {
3016 pub addr: std::net::IpAddr,
3017}
3018
3019#[cfg(feature = "net")]
3021#[derive(Facet, Debug, Clone, PartialEq)]
3022#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3023pub struct Ipv4AddrWrapper {
3024 pub addr: std::net::Ipv4Addr,
3025}
3026
3027#[cfg(feature = "net")]
3029#[derive(Facet, Debug, Clone, PartialEq)]
3030#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3031pub struct Ipv6AddrWrapper {
3032 pub addr: std::net::Ipv6Addr,
3033}
3034
3035#[cfg(feature = "net")]
3037#[derive(Facet, Debug, Clone, PartialEq)]
3038#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3039pub struct SocketAddrV4Wrapper {
3040 pub addr: std::net::SocketAddr,
3041}
3042
3043#[cfg(feature = "net")]
3045#[derive(Facet, Debug, Clone, PartialEq)]
3046#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3047pub struct SocketAddrV6Wrapper {
3048 pub addr: std::net::SocketAddr,
3049}
3050
3051#[cfg(feature = "net")]
3053#[derive(Facet, Debug, Clone, PartialEq)]
3054#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3055pub struct SocketAddrV4ExplicitWrapper {
3056 pub addr: std::net::SocketAddrV4,
3057}
3058
3059#[cfg(feature = "net")]
3061#[derive(Facet, Debug, Clone, PartialEq)]
3062#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3063pub struct SocketAddrV6ExplicitWrapper {
3064 pub addr: std::net::SocketAddrV6,
3065}
3066
3067#[derive(Facet, Debug, Clone, PartialEq)]
3071#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3072pub struct MapWrapper {
3073 pub data: std::collections::BTreeMap<String, i32>,
3074}
3075
3076#[derive(Facet, Debug, Clone, PartialEq)]
3078#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3079pub struct TupleWrapper {
3080 pub triple: (String, i32, bool),
3081}
3082
3083#[derive(Facet, Debug, Clone, PartialEq)]
3085#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3086pub struct NestedTupleWrapper {
3087 pub outer: ((i32, i32), (String, bool)),
3088}
3089
3090#[derive(Facet, Debug, Clone, PartialEq)]
3092#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3093pub struct EmptyTupleWrapper {
3094 pub name: String,
3095 pub empty: (),
3096}
3097
3098#[derive(Facet, Debug, Clone, PartialEq)]
3100#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3101pub struct SingleElementTupleWrapper {
3102 pub name: String,
3103 pub single: (i32,),
3104}
3105
3106#[derive(Facet, Debug, Clone, PartialEq)]
3110#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3111#[repr(u8)]
3112pub enum UnitVariantEnum {
3113 Active,
3114 Inactive,
3115 Pending,
3116}
3117
3118#[derive(Facet, Debug, Clone, PartialEq)]
3120#[repr(u8)]
3121pub enum EnumVariantRename {
3122 #[facet(rename = "enabled")]
3123 Active,
3124 #[facet(rename = "disabled")]
3125 Inactive,
3126}
3127
3128#[derive(Facet, Debug, Clone, PartialEq)]
3130#[facet(is_numeric)]
3131#[repr(u8)]
3132pub enum NumericEnum {
3133 A,
3134 B,
3135}
3136
3137#[derive(Facet, Debug, Clone, PartialEq)]
3139#[facet(is_numeric)]
3140#[repr(i16)]
3141pub enum SignedNumericEnum {
3142 Z = -1,
3143 A,
3144}
3145
3146#[derive(Facet, Debug, Clone, PartialEq)]
3148#[facet(untagged)]
3149#[repr(u8)]
3150pub enum UntaggedEnum {
3151 Point { x: i32, y: i32 },
3152 Value(i64),
3153}
3154
3155#[derive(Facet, Debug, Clone, PartialEq)]
3157#[facet(untagged)]
3158#[repr(u8)]
3159pub enum UntaggedWithNull {
3160 None,
3161 Some(i32),
3162}
3163
3164#[derive(Facet, Debug, Clone, PartialEq)]
3166#[facet(untagged)]
3167#[repr(u8)]
3168pub enum UntaggedNewtype {
3169 String(String),
3170 Number(u64),
3171 Bool(bool),
3172}
3173
3174#[derive(Facet, Debug, Clone, PartialEq)]
3176pub struct UntaggedAsField {
3177 pub name: String,
3178 pub value: UntaggedNewtype,
3179}
3180
3181#[derive(Facet, Debug, Clone, PartialEq)]
3184#[facet(untagged)]
3185#[repr(u8)]
3186pub enum UntaggedUnitOnly {
3187 Alpha,
3188 Beta,
3189 Gamma,
3190}
3191
3192#[derive(Facet, Debug, Clone, PartialEq)]
3194#[repr(u8)]
3195pub enum TupleVariantEnum {
3196 Empty,
3197 Pair(String, i32),
3198 Triple(bool, f64, String),
3199}
3200
3201#[derive(Facet, Debug, Clone, PartialEq)]
3203#[repr(u8)]
3204pub enum NewtypeVariantEnum {
3205 None,
3206 Some(i32),
3207}
3208
3209#[derive(Facet, Debug, Clone, PartialEq)]
3213#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3214pub struct BoxWrapper {
3215 pub inner: Box<i32>,
3216}
3217
3218#[derive(Facet, Debug, Clone, PartialEq)]
3220#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3221pub struct ArcWrapper {
3222 pub inner: std::sync::Arc<i32>,
3223}
3224
3225#[derive(Facet, Debug, Clone, PartialEq)]
3227#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3228pub struct RcWrapper {
3229 pub inner: std::rc::Rc<i32>,
3230}
3231
3232#[derive(Facet, Debug, Clone, PartialEq)]
3234pub struct BoxStrWrapper {
3235 pub inner: Box<str>,
3236}
3237
3238#[derive(Facet, Debug, Clone, PartialEq)]
3240pub struct ArcStrWrapper {
3241 pub inner: std::sync::Arc<str>,
3242}
3243
3244#[derive(Facet, Debug, Clone, PartialEq)]
3246pub struct RcStrWrapper {
3247 pub inner: std::rc::Rc<str>,
3248}
3249
3250#[derive(Facet, Debug, Clone, PartialEq)]
3252pub struct ArcSliceWrapper {
3253 pub inner: std::sync::Arc<[i32]>,
3254}
3255
3256#[cfg(feature = "yoke")]
3260#[derive(Facet, Debug)]
3261pub struct YokeWrapper {
3262 pub value: yoke::Yoke<std::borrow::Cow<'static, str>, std::sync::Arc<str>>,
3263}
3264
3265#[cfg(feature = "yoke")]
3266impl PartialEq for YokeWrapper {
3267 fn eq(&self, other: &Self) -> bool {
3268 self.value.get() == other.value.get()
3269 }
3270}
3271
3272#[cfg(feature = "yoke")]
3274#[derive(Facet, Debug)]
3275pub struct YokingWrapper {
3276 pub value: yoke::Yoke<SplittingYoker<'static>, std::sync::Arc<str>>,
3277}
3278
3279#[cfg(feature = "yoke")]
3280impl PartialEq for YokingWrapper {
3281 fn eq(&self, other: &Self) -> bool {
3282 self.value.get() == other.value.get()
3283 }
3284}
3285
3286#[cfg(feature = "yoke")]
3287#[derive(Facet, Debug, PartialEq, yoke::Yokeable)]
3288#[facet(try_from_ref = SplittingYoker::try_from_ref)]
3289pub struct SplittingYoker<'a> {
3290 left: &'a str,
3291 right: &'a str,
3292}
3293
3294#[cfg(feature = "yoke")]
3295impl<'a> SplittingYoker<'a> {
3296 pub fn try_from_ref(value: &'a str) -> Result<Self, &'static str> {
3297 if let Some((left, right)) = value.split_once('|') {
3298 Ok(SplittingYoker { left, right })
3299 } else {
3300 Err("Invalid format")
3301 }
3302 }
3303}
3304
3305#[derive(Facet, Debug, Clone, PartialEq)]
3309#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3310pub struct SetWrapper {
3311 pub items: std::collections::BTreeSet<String>,
3312}
3313
3314#[derive(Facet, Debug, Clone, PartialEq)]
3318#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3319pub struct IntegerTypes16 {
3320 pub signed_16: i16,
3321 pub unsigned_16: u16,
3322}
3323
3324#[derive(Facet, Debug, Clone, PartialEq)]
3326#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3327pub struct IntegerTypes128 {
3328 pub signed_128: i128,
3329 pub unsigned_128: u128,
3330}
3331
3332#[derive(Facet, Debug, Clone, PartialEq)]
3334#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3335pub struct IntegerTypesSize {
3336 pub signed_size: isize,
3337 pub unsigned_size: usize,
3338}
3339
3340#[derive(Facet, Debug, Clone, PartialEq)]
3344#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3345pub struct NonZeroTypes {
3346 pub nz_u32: std::num::NonZeroU32,
3347 pub nz_i64: std::num::NonZeroI64,
3348}
3349
3350#[derive(Facet, Debug, Clone, PartialEq)]
3352#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3353pub struct NonZeroTypesExtended {
3354 pub nz_u8: std::num::NonZeroU8,
3355 pub nz_i8: std::num::NonZeroI8,
3356 pub nz_u16: std::num::NonZeroU16,
3357 pub nz_i16: std::num::NonZeroI16,
3358 pub nz_u128: std::num::NonZeroU128,
3359 pub nz_i128: std::num::NonZeroI128,
3360 pub nz_usize: std::num::NonZeroUsize,
3361 pub nz_isize: std::num::NonZeroIsize,
3362}
3363
3364#[derive(Facet, Debug, Clone, PartialEq)]
3368#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3369pub struct CowStrWrapper {
3370 pub owned: std::borrow::Cow<'static, str>,
3371 pub message: std::borrow::Cow<'static, str>,
3372}
3373
3374#[derive(Facet, Debug, Clone, PartialEq)]
3378#[facet(transparent)]
3379pub struct NewtypeU64(pub u64);
3380
3381#[derive(Facet, Debug, Clone, PartialEq)]
3383pub struct NewtypeU64Wrapper {
3384 pub value: NewtypeU64,
3385}
3386
3387#[derive(Facet, Debug, Clone, PartialEq)]
3389#[facet(transparent)]
3390pub struct NewtypeString(pub String);
3391
3392#[derive(Facet, Debug, Clone, PartialEq)]
3394pub struct NewtypeStringWrapper {
3395 pub value: NewtypeString,
3396}
3397
3398#[derive(Facet, Debug, Clone, PartialEq)]
3402#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3403pub struct CharWrapper {
3404 pub letter: char,
3405 pub emoji: char,
3406}
3407
3408#[derive(Facet, Debug, Clone, PartialEq)]
3412#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3413pub struct HashSetWrapper {
3414 pub items: std::collections::HashSet<String>,
3415}
3416
3417#[derive(Facet, Debug, Clone, PartialEq)]
3421#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3422pub struct NestedVecWrapper {
3423 pub matrix: Vec<Vec<i32>>,
3424}
3425
3426#[derive(Facet, Debug, Clone, PartialEq)]
3430#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3431pub struct BytesWrapper {
3432 pub data: Vec<u8>,
3433}
3434
3435#[derive(Facet, Debug, Clone, PartialEq)]
3439#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3440pub struct ArrayWrapper {
3441 pub values: [u64; 3],
3442}
3443
3444#[derive(Facet, Debug, Clone, PartialEq)]
3448#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3449pub struct SkipUnknownStruct {
3450 pub known: String,
3451}
3452
3453#[derive(Facet, Debug, Clone, PartialEq)]
3457pub struct StringEscapes {
3458 pub text: String,
3459}
3460
3461#[derive(Facet, Debug, Clone, PartialEq)]
3463pub struct StringEscapesExtended {
3464 pub backspace: String,
3465 pub formfeed: String,
3466 pub carriage_return: String,
3467 pub control_char: String,
3468}
3469
3470#[derive(Facet, Debug, Clone, PartialEq)]
3474#[cfg_attr(feature = "msgpack", derive(serde::Serialize, serde::Deserialize))]
3475pub struct UnitStruct;
3476
3477#[cfg(feature = "uuid")]
3480const CASE_UUID: CaseDescriptor<UuidWrapper> = CaseDescriptor {
3481 id: "third_party::uuid",
3482 description: "uuid::Uuid type",
3483 expected: || UuidWrapper {
3484 id: uuid::Uuid::from_bytes([
3485 0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, 0xa7, 0x16, 0x44, 0x66, 0x55, 0x44,
3486 0x00, 0x00,
3487 ]),
3488 },
3489};
3490
3491#[cfg(feature = "ulid")]
3492const CASE_ULID: CaseDescriptor<UlidWrapper> = CaseDescriptor {
3493 id: "third_party::ulid",
3494 description: "ulid::Ulid type",
3495 expected: || UlidWrapper {
3496 id: ulid::Ulid::from_string("01ARZ3NDEKTSV4RRFFQ69G5FAV").unwrap(),
3497 },
3498};
3499
3500#[cfg(feature = "camino")]
3501const CASE_CAMINO_PATH: CaseDescriptor<CaminoWrapper> = CaseDescriptor {
3502 id: "third_party::camino",
3503 description: "camino::Utf8PathBuf type",
3504 expected: || CaminoWrapper {
3505 path: camino::Utf8PathBuf::from("/home/user/documents"),
3506 },
3507};
3508
3509#[cfg(feature = "ordered-float")]
3510const CASE_ORDERED_FLOAT: CaseDescriptor<OrderedFloatWrapper> = CaseDescriptor {
3511 id: "third_party::ordered_float",
3512 description: "ordered_float::OrderedFloat type",
3513 expected: || OrderedFloatWrapper {
3514 value: ordered_float::OrderedFloat(1.23456),
3515 },
3516};
3517
3518#[cfg(feature = "rust_decimal")]
3519const CASE_RUST_DECIMAL: CaseDescriptor<RustDecimalWrapper> = CaseDescriptor {
3520 id: "third_party::rust_decimal",
3521 description: "rust_decimal::Decimal type",
3522 expected: || RustDecimalWrapper {
3523 amount: rust_decimal::Decimal::new(2499, 2), },
3525};
3526
3527#[cfg(feature = "time")]
3528const CASE_TIME_OFFSET_DATETIME: CaseDescriptor<TimeOffsetDateTimeWrapper> = CaseDescriptor {
3529 id: "third_party::time_offset_datetime",
3530 description: "time::OffsetDateTime type",
3531 expected: || TimeOffsetDateTimeWrapper {
3532 created_at: time::macros::datetime!(2023-01-15 12:34:56 UTC),
3533 },
3534};
3535
3536#[cfg(feature = "jiff02")]
3537const CASE_JIFF_TIMESTAMP: CaseDescriptor<JiffTimestampWrapper> = CaseDescriptor {
3538 id: "third_party::jiff_timestamp",
3539 description: "jiff::Timestamp type",
3540 expected: || JiffTimestampWrapper {
3541 created_at: "2023-12-31T11:30:00Z".parse().unwrap(),
3542 },
3543};
3544
3545#[cfg(feature = "jiff02")]
3546const CASE_JIFF_CIVIL_DATETIME: CaseDescriptor<JiffCivilDateTimeWrapper> = CaseDescriptor {
3547 id: "third_party::jiff_civil_datetime",
3548 description: "jiff::civil::DateTime type",
3549 expected: || JiffCivilDateTimeWrapper {
3550 created_at: "2024-06-19T15:22:45".parse().unwrap(),
3551 },
3552};
3553
3554#[cfg(feature = "chrono")]
3555const CASE_CHRONO_DATETIME_UTC: CaseDescriptor<ChronoDateTimeUtcWrapper> = CaseDescriptor {
3556 id: "third_party::chrono_datetime_utc",
3557 description: "chrono::DateTime<Utc> type",
3558 expected: || ChronoDateTimeUtcWrapper {
3559 created_at: chrono::TimeZone::with_ymd_and_hms(&chrono::Utc, 2023, 1, 15, 12, 34, 56)
3560 .unwrap(),
3561 },
3562};
3563
3564#[cfg(feature = "chrono")]
3565const CASE_CHRONO_NAIVE_DATETIME: CaseDescriptor<ChronoNaiveDateTimeWrapper> = CaseDescriptor {
3566 id: "third_party::chrono_naive_datetime",
3567 description: "chrono::NaiveDateTime type",
3568 expected: || ChronoNaiveDateTimeWrapper {
3569 created_at: chrono::NaiveDate::from_ymd_opt(2023, 1, 15)
3570 .unwrap()
3571 .and_hms_opt(12, 34, 56)
3572 .unwrap(),
3573 },
3574};
3575
3576#[cfg(feature = "chrono")]
3577const CASE_CHRONO_NAIVE_DATE: CaseDescriptor<ChronoNaiveDateWrapper> = CaseDescriptor {
3578 id: "third_party::chrono_naive_date",
3579 description: "chrono::NaiveDate type",
3580 expected: || ChronoNaiveDateWrapper {
3581 birth_date: chrono::NaiveDate::from_ymd_opt(2023, 1, 15).unwrap(),
3582 },
3583};
3584
3585#[cfg(feature = "chrono")]
3586const CASE_CHRONO_NAIVE_TIME: CaseDescriptor<ChronoNaiveTimeWrapper> = CaseDescriptor {
3587 id: "third_party::chrono_naive_time",
3588 description: "chrono::NaiveTime type",
3589 expected: || ChronoNaiveTimeWrapper {
3590 alarm_time: chrono::NaiveTime::from_hms_opt(12, 34, 56).unwrap(),
3591 },
3592};
3593
3594#[cfg(feature = "chrono")]
3595const CASE_CHRONO_IN_VEC: CaseDescriptor<ChronoInVecWrapper> = CaseDescriptor {
3596 id: "third_party::chrono_in_vec",
3597 description: "Vec<chrono::DateTime<Utc>> - chrono in collections",
3598 expected: || ChronoInVecWrapper {
3599 timestamps: vec![
3600 chrono::TimeZone::with_ymd_and_hms(&chrono::Utc, 2023, 1, 1, 0, 0, 0).unwrap(),
3601 chrono::TimeZone::with_ymd_and_hms(&chrono::Utc, 2023, 6, 15, 12, 30, 0).unwrap(),
3602 ],
3603 },
3604};
3605
3606#[cfg(feature = "chrono")]
3607const CASE_CHRONO_DURATION: CaseDescriptor<ChronoDurationWrapper> = CaseDescriptor {
3608 id: "third_party::chrono_duration",
3609 description: "chrono::Duration type - serializes as (secs, nanos) tuple",
3610 expected: || ChronoDurationWrapper {
3611 duration: chrono::Duration::seconds(3600) + chrono::Duration::nanoseconds(500_000_000),
3612 },
3613};
3614
3615#[cfg(feature = "chrono")]
3616const CASE_CHRONO_DURATION_NEGATIVE: CaseDescriptor<ChronoDurationNegativeWrapper> =
3617 CaseDescriptor {
3618 id: "third_party::chrono_duration_negative",
3619 description: "chrono::Duration negative value - tests signed (secs, nanos) handling",
3620 expected: || ChronoDurationNegativeWrapper {
3621 duration: chrono::Duration::seconds(-90) + chrono::Duration::nanoseconds(-250_000_000),
3622 },
3623 };
3624
3625const CASE_STD_DURATION: CaseDescriptor<StdDurationWrapper> = CaseDescriptor {
3628 id: "std::std_duration",
3629 description: "core::time::Duration type - serializes as (secs, nanos) tuple",
3630 expected: || StdDurationWrapper {
3631 duration: core::time::Duration::new(3600, 500_000_000),
3632 },
3633};
3634
3635#[cfg(feature = "bytes")]
3638const CASE_BYTES_BYTES: CaseDescriptor<BytesBytesWrapper> = CaseDescriptor {
3639 id: "third_party::bytes_bytes",
3640 description: "bytes::Bytes type",
3641 expected: || BytesBytesWrapper {
3642 data: bytes::Bytes::from_static(&[1, 2, 3, 4, 255]),
3643 },
3644};
3645
3646#[cfg(feature = "bytes")]
3647const CASE_BYTES_BYTES_MUT: CaseDescriptor<BytesBytesMutWrapper> = CaseDescriptor {
3648 id: "third_party::bytes_bytes_mut",
3649 description: "bytes::BytesMut type",
3650 expected: || BytesBytesMutWrapper {
3651 data: bytes::BytesMut::from(&[1, 2, 3, 4, 255][..]),
3652 },
3653};
3654
3655#[cfg(feature = "bytestring")]
3658const CASE_BYTESTRING: CaseDescriptor<ByteStringWrapper> = CaseDescriptor {
3659 id: "third_party::bytestring",
3660 description: "bytestring::ByteString type",
3661 expected: || ByteStringWrapper {
3662 value: bytestring::ByteString::from("hello world"),
3663 },
3664};
3665
3666#[cfg(feature = "compact_str")]
3667const CASE_COMPACT_STRING: CaseDescriptor<CompactStringWrapper> = CaseDescriptor {
3668 id: "third_party::compact_string",
3669 description: "compact_str::CompactString type",
3670 expected: || CompactStringWrapper {
3671 value: compact_str::CompactString::from("hello world"),
3672 },
3673};
3674
3675#[cfg(feature = "smartstring")]
3676const CASE_SMARTSTRING: CaseDescriptor<SmartStringWrapper> = CaseDescriptor {
3677 id: "third_party::smartstring",
3678 description: "smartstring::SmartString type",
3679 expected: || SmartStringWrapper {
3680 value: smartstring::SmartString::from("hello world"),
3681 },
3682};
3683
3684#[cfg(feature = "smol_str")]
3685const CASE_SMOL_STR: CaseDescriptor<SmolStrWrapper> = CaseDescriptor {
3686 id: "third_party::smol_str",
3687 description: "smol_str::SmolStr type",
3688 expected: || SmolStrWrapper {
3689 value: smol_str::SmolStr::from("hello world"),
3690 },
3691};
3692
3693#[cfg(feature = "iddqd")]
3694const CASE_IDDQD_ID_HASH_MAP: CaseDescriptor<IddqdIdHashMapWrapper> = CaseDescriptor {
3695 id: "third_party::iddqd_id_hash_map",
3696 description: "iddqd::IdHashMap type (set-like collection with key extraction)",
3697 expected: || {
3698 let mut map = iddqd::IdHashMap::with_hasher(std::hash::RandomState::new());
3699 map.insert_overwrite(IddqdTestItem {
3700 id: 1,
3701 name: String::from("Alice"),
3702 });
3703 IddqdIdHashMapWrapper { items: map }
3704 },
3705};
3706
3707#[cfg(feature = "iddqd")]
3708const CASE_IDDQD_ID_ORD_MAP: CaseDescriptor<IddqdIdOrdMapWrapper> = CaseDescriptor {
3709 id: "third_party::iddqd_id_ord_map",
3710 description: "iddqd::IdOrdMap type (ordered set-like collection with key extraction)",
3711 expected: || {
3712 let mut map = iddqd::IdOrdMap::new();
3713 map.insert_overwrite(IddqdOrdTestItem {
3714 id: 1,
3715 name: String::from("Alice"),
3716 });
3717 IddqdIdOrdMapWrapper { items: map }
3718 },
3719};
3720
3721#[cfg(feature = "iddqd")]
3722const CASE_IDDQD_BI_HASH_MAP: CaseDescriptor<IddqdBiHashMapWrapper> = CaseDescriptor {
3723 id: "third_party::iddqd_bi_hash_map",
3724 description: "iddqd::BiHashMap type (bijective map with two keys per value)",
3725 expected: || {
3726 let mut map = iddqd::BiHashMap::with_hasher(std::hash::RandomState::new());
3727 map.insert_overwrite(IddqdBiTestItem {
3728 id: 1,
3729 code: String::from("A001"),
3730 name: String::from("Alice"),
3731 });
3732 IddqdBiHashMapWrapper { items: map }
3733 },
3734};
3735
3736#[cfg(feature = "iddqd")]
3737const CASE_IDDQD_TRI_HASH_MAP: CaseDescriptor<IddqdTriHashMapWrapper> = CaseDescriptor {
3738 id: "third_party::iddqd_tri_hash_map",
3739 description: "iddqd::TriHashMap type (trijective map with three keys per value)",
3740 expected: || {
3741 let mut map = iddqd::TriHashMap::with_hasher(std::hash::RandomState::new());
3742 map.insert_overwrite(IddqdTriTestItem {
3743 id: 1,
3744 code: String::from("A001"),
3745 email: String::from("alice@example.com"),
3746 name: String::from("Alice"),
3747 });
3748 IddqdTriHashMapWrapper { items: map }
3749 },
3750};
3751
3752#[cfg(feature = "facet-value")]
3755const CASE_VALUE_NULL: CaseDescriptor<facet_value::Value> = CaseDescriptor {
3756 id: "value::null",
3757 description: "facet_value::Value - null",
3758 expected: || facet_value::Value::NULL,
3759};
3760
3761#[cfg(feature = "facet-value")]
3762const CASE_VALUE_BOOL: CaseDescriptor<facet_value::Value> = CaseDescriptor {
3763 id: "value::bool",
3764 description: "facet_value::Value - bool",
3765 expected: || facet_value::Value::TRUE,
3766};
3767
3768#[cfg(feature = "facet-value")]
3769const CASE_VALUE_INTEGER: CaseDescriptor<facet_value::Value> = CaseDescriptor {
3770 id: "value::integer",
3771 description: "facet_value::Value - integer",
3772 expected: || facet_value::Value::from(42i64),
3773};
3774
3775#[cfg(feature = "facet-value")]
3776const CASE_VALUE_FLOAT: CaseDescriptor<facet_value::Value> = CaseDescriptor {
3777 id: "value::float",
3778 description: "facet_value::Value - float",
3779 expected: || facet_value::Value::from(2.5f64),
3780};
3781
3782#[cfg(feature = "facet-value")]
3783const CASE_VALUE_STRING: CaseDescriptor<facet_value::Value> = CaseDescriptor {
3784 id: "value::string",
3785 description: "facet_value::Value - string",
3786 expected: || facet_value::Value::from("hello world"),
3787};
3788
3789#[cfg(feature = "facet-value")]
3790const CASE_VALUE_ARRAY: CaseDescriptor<facet_value::Value> = CaseDescriptor {
3791 id: "value::array",
3792 description: "facet_value::Value - array",
3793 expected: || {
3794 facet_value::VArray::from_iter([
3795 facet_value::Value::from(1i64),
3796 facet_value::Value::from(2i64),
3797 facet_value::Value::from(3i64),
3798 ])
3799 .into()
3800 },
3801};
3802
3803#[cfg(feature = "facet-value")]
3804const CASE_VALUE_OBJECT: CaseDescriptor<facet_value::Value> = CaseDescriptor {
3805 id: "value::object",
3806 description: "facet_value::Value - object",
3807 expected: || {
3808 let mut map = facet_value::VObject::new();
3809 map.insert("name", facet_value::Value::from("test"));
3810 map.insert("count", facet_value::Value::from(42i64));
3811 map.into()
3812 },
3813};
3814
3815#[cfg(feature = "uuid")]
3819#[derive(Facet, Debug, Clone, PartialEq)]
3820pub struct UuidWrapper {
3821 pub id: uuid::Uuid,
3822}
3823
3824#[cfg(feature = "ulid")]
3826#[derive(Facet, Debug, Clone, PartialEq)]
3827pub struct UlidWrapper {
3828 pub id: ulid::Ulid,
3829}
3830
3831#[cfg(feature = "camino")]
3833#[derive(Facet, Debug, Clone, PartialEq)]
3834pub struct CaminoWrapper {
3835 pub path: camino::Utf8PathBuf,
3836}
3837
3838#[cfg(feature = "ordered-float")]
3840#[derive(Facet, Debug, Clone, PartialEq)]
3841pub struct OrderedFloatWrapper {
3842 pub value: ordered_float::OrderedFloat<f64>,
3843}
3844
3845#[cfg(feature = "rust_decimal")]
3847#[derive(Facet, Debug, Clone, PartialEq)]
3848pub struct RustDecimalWrapper {
3849 pub amount: rust_decimal::Decimal,
3850}
3851
3852#[cfg(feature = "time")]
3854#[derive(Facet, Debug, Clone, PartialEq)]
3855pub struct TimeOffsetDateTimeWrapper {
3856 pub created_at: time::OffsetDateTime,
3857}
3858
3859#[cfg(feature = "jiff02")]
3861#[derive(Facet, Debug, Clone, PartialEq)]
3862pub struct JiffTimestampWrapper {
3863 pub created_at: jiff::Timestamp,
3864}
3865
3866#[cfg(feature = "jiff02")]
3868#[derive(Facet, Debug, Clone, PartialEq)]
3869pub struct JiffCivilDateTimeWrapper {
3870 pub created_at: jiff::civil::DateTime,
3871}
3872
3873#[cfg(feature = "chrono")]
3875#[derive(Facet, Debug, Clone, PartialEq)]
3876pub struct ChronoDateTimeUtcWrapper {
3877 pub created_at: chrono::DateTime<chrono::Utc>,
3878}
3879
3880#[cfg(feature = "chrono")]
3882#[derive(Facet, Debug, Clone, PartialEq)]
3883pub struct ChronoNaiveDateTimeWrapper {
3884 pub created_at: chrono::NaiveDateTime,
3885}
3886
3887#[cfg(feature = "chrono")]
3889#[derive(Facet, Debug, Clone, PartialEq)]
3890pub struct ChronoNaiveDateWrapper {
3891 pub birth_date: chrono::NaiveDate,
3892}
3893
3894#[cfg(feature = "chrono")]
3896#[derive(Facet, Debug, Clone, PartialEq)]
3897pub struct ChronoNaiveTimeWrapper {
3898 pub alarm_time: chrono::NaiveTime,
3899}
3900
3901#[cfg(feature = "chrono")]
3903#[derive(Facet, Debug, Clone, PartialEq)]
3904pub struct ChronoInVecWrapper {
3905 pub timestamps: Vec<chrono::DateTime<chrono::Utc>>,
3906}
3907
3908#[cfg(feature = "chrono")]
3910#[derive(Facet, Debug, Clone, PartialEq)]
3911pub struct ChronoDurationWrapper {
3912 pub duration: chrono::Duration,
3913}
3914
3915#[cfg(feature = "chrono")]
3917#[derive(Facet, Debug, Clone, PartialEq)]
3918pub struct ChronoDurationNegativeWrapper {
3919 pub duration: chrono::Duration,
3920}
3921
3922#[derive(Facet, Debug, Clone, PartialEq)]
3926pub struct StdDurationWrapper {
3927 pub duration: core::time::Duration,
3928}
3929
3930#[cfg(feature = "bytes")]
3934#[derive(Facet, Debug, Clone, PartialEq)]
3935pub struct BytesBytesWrapper {
3936 pub data: bytes::Bytes,
3937}
3938
3939#[cfg(feature = "bytes")]
3941#[derive(Facet, Debug, Clone, PartialEq)]
3942pub struct BytesBytesMutWrapper {
3943 pub data: bytes::BytesMut,
3944}
3945
3946#[cfg(feature = "bytestring")]
3950#[derive(Facet, Debug, Clone, PartialEq)]
3951pub struct ByteStringWrapper {
3952 pub value: bytestring::ByteString,
3953}
3954
3955#[cfg(feature = "compact_str")]
3957#[derive(Facet, Debug, Clone, PartialEq)]
3958pub struct CompactStringWrapper {
3959 pub value: compact_str::CompactString,
3960}
3961
3962#[cfg(feature = "smartstring")]
3964#[derive(Facet, Debug, Clone, PartialEq)]
3965pub struct SmartStringWrapper {
3966 pub value: smartstring::SmartString<smartstring::LazyCompact>,
3967}
3968
3969#[cfg(feature = "smol_str")]
3971#[derive(Facet, Debug, Clone, PartialEq)]
3972pub struct SmolStrWrapper {
3973 pub value: smol_str::SmolStr,
3974}
3975
3976#[cfg(feature = "iddqd")]
3981#[derive(Facet, Debug, Clone, PartialEq)]
3982pub struct IddqdTestItem {
3983 pub id: u64,
3984 pub name: String,
3985}
3986
3987#[cfg(feature = "iddqd")]
3988impl iddqd::IdHashItem for IddqdTestItem {
3989 type Key<'a> = u64;
3990 fn key(&self) -> Self::Key<'_> {
3991 self.id
3992 }
3993 iddqd::id_upcast!();
3994}
3995
3996#[cfg(feature = "iddqd")]
3999#[derive(Facet, Debug, Clone, PartialEq)]
4000pub struct IddqdIdHashMapWrapper {
4001 pub items: iddqd::IdHashMap<IddqdTestItem, std::hash::RandomState>,
4002}
4003
4004#[cfg(feature = "iddqd")]
4007#[derive(Facet, Debug, Clone, PartialEq)]
4008pub struct IddqdOrdTestItem {
4009 pub id: u64,
4010 pub name: String,
4011}
4012
4013#[cfg(feature = "iddqd")]
4014impl iddqd::IdOrdItem for IddqdOrdTestItem {
4015 type Key<'a> = u64;
4016 fn key(&self) -> Self::Key<'_> {
4017 self.id
4018 }
4019 iddqd::id_upcast!();
4020}
4021
4022#[cfg(feature = "iddqd")]
4024#[derive(Facet, Debug, Clone, PartialEq)]
4025pub struct IddqdIdOrdMapWrapper {
4026 pub items: iddqd::IdOrdMap<IddqdOrdTestItem>,
4027}
4028
4029#[cfg(feature = "iddqd")]
4032#[derive(Facet, Debug, Clone, PartialEq)]
4033pub struct IddqdBiTestItem {
4034 pub id: u64,
4035 pub code: String,
4036 pub name: String,
4037}
4038
4039#[cfg(feature = "iddqd")]
4040impl iddqd::BiHashItem for IddqdBiTestItem {
4041 type K1<'a> = u64;
4042 type K2<'a> = &'a str;
4043 fn key1(&self) -> Self::K1<'_> {
4044 self.id
4045 }
4046 fn key2(&self) -> Self::K2<'_> {
4047 &self.code
4048 }
4049 iddqd::bi_upcast!();
4050}
4051
4052#[cfg(feature = "iddqd")]
4055#[derive(Facet, Debug, Clone, PartialEq)]
4056pub struct IddqdBiHashMapWrapper {
4057 pub items: iddqd::BiHashMap<IddqdBiTestItem, std::hash::RandomState>,
4058}
4059
4060#[cfg(feature = "iddqd")]
4063#[derive(Facet, Debug, Clone, PartialEq)]
4064pub struct IddqdTriTestItem {
4065 pub id: u64,
4066 pub code: String,
4067 pub email: String,
4068 pub name: String,
4069}
4070
4071#[cfg(feature = "iddqd")]
4072impl iddqd::TriHashItem for IddqdTriTestItem {
4073 type K1<'a> = u64;
4074 type K2<'a> = &'a str;
4075 type K3<'a> = &'a str;
4076 fn key1(&self) -> Self::K1<'_> {
4077 self.id
4078 }
4079 fn key2(&self) -> Self::K2<'_> {
4080 &self.code
4081 }
4082 fn key3(&self) -> Self::K3<'_> {
4083 &self.email
4084 }
4085 iddqd::tri_upcast!();
4086}
4087
4088#[cfg(feature = "iddqd")]
4091#[derive(Facet, Debug, Clone, PartialEq)]
4092pub struct IddqdTriHashMapWrapper {
4093 pub items: iddqd::TriHashMap<IddqdTriTestItem, std::hash::RandomState>,
4094}
4095
4096fn emit_case_showcase<S, T>(
4097 desc: &'static CaseDescriptor<T>,
4098 note: Option<&'static str>,
4099 roundtrip_disabled_reason: Option<&'static str>,
4100 input: &'static [u8],
4101 highlight_language: Option<&'static str>,
4102 actual: &T,
4103) where
4104 S: FormatSuite,
4105 for<'facet> T: Facet<'facet>,
4106 T: Debug,
4107{
4108 let (input_label, input_block) = match highlight_language {
4109 Some(language) => match highlight_payload(language, input) {
4110 Some(html) => (format!("Input highlighted via arborium ({language})"), html),
4111 None => (
4112 format!("Input (UTF-8, highlighting unavailable for {language})"),
4113 String::from_utf8_lossy(input).into_owned(),
4114 ),
4115 },
4116 None => (
4117 "Input (UTF-8)".to_string(),
4118 String::from_utf8_lossy(input).into_owned(),
4119 ),
4120 };
4121
4122 let pretty_output = format!(
4123 "{}",
4124 actual.pretty_with(PrettyPrinter::new().with_indent_size(2))
4125 );
4126 let note_line = note.map(|n| format!("note: {n}\n")).unwrap_or_default();
4127 let roundtrip_line = roundtrip_disabled_reason
4128 .map(|r| format!("roundtrip: disabled ({r})\n"))
4129 .unwrap_or_default();
4130
4131 println!(
4132 "{}",
4133 formatdoc!(
4134 "
4135 ── facet-format-suite :: {format_name} :: {case_id} ──
4136 description: {description}
4137 {note_line}{roundtrip_line}{input_label}:
4138 {input_block}
4139
4140 facet-pretty output:
4141 {pretty_output}
4142 ",
4143 format_name = S::format_name(),
4144 case_id = desc.id,
4145 description = desc.description,
4146 note_line = note_line,
4147 roundtrip_line = roundtrip_line,
4148 input_label = input_label,
4149 input_block = input_block,
4150 pretty_output = pretty_output,
4151 )
4152 );
4153}
4154
4155fn emit_error_case_showcase<S: FormatSuite>(
4156 case_id: &str,
4157 description: &str,
4158 note: Option<&'static str>,
4159 input: &[u8],
4160 highlight_language: Option<&'static str>,
4161 error_contains: &str,
4162) {
4163 let (input_label, input_block) = match highlight_language {
4164 Some(language) => match highlight_payload(language, input) {
4165 Some(html) => (format!("Input highlighted via arborium ({language})"), html),
4166 None => (
4167 format!("Input (UTF-8, highlighting unavailable for {language})"),
4168 String::from_utf8_lossy(input).into_owned(),
4169 ),
4170 },
4171 None => (
4172 "Input (UTF-8)".to_string(),
4173 String::from_utf8_lossy(input).into_owned(),
4174 ),
4175 };
4176
4177 let note_line = note.map(|n| format!("note: {n}\n")).unwrap_or_default();
4178
4179 println!(
4180 "{}",
4181 formatdoc!(
4182 "
4183 ── facet-format-suite :: {format_name} :: {case_id} ──
4184 description: {description}
4185 {note_line}expects error containing: \"{error_contains}\"
4186 {input_label}:
4187 {input_block}
4188 ",
4189 format_name = S::format_name(),
4190 case_id = case_id,
4191 description = description,
4192 note_line = note_line,
4193 error_contains = error_contains,
4194 input_label = input_label,
4195 input_block = input_block,
4196 )
4197 );
4198}
4199
4200fn highlight_payload(language: &str, input: &[u8]) -> Option<String> {
4201 let source = core::str::from_utf8(input).ok()?;
4202 let mut highlighter = Highlighter::new();
4203 highlighter.highlight(language, source).ok()
4204}
4205
4206#[cfg(feature = "msgpack")]
4214pub mod msgpack {
4215 use super::*;
4216
4217 pub fn serialize<T: serde::Serialize>(value: &T) -> Vec<u8> {
4222 rmp_serde::to_vec_named(value).expect("rmp-serde serialization failed")
4223 }
4224
4225 pub fn case_from_expected<T>(expected: &T) -> CaseSpec
4230 where
4231 T: serde::Serialize,
4232 {
4233 CaseSpec::from_bytes_vec(serialize(expected))
4234 }
4235
4236 pub fn struct_single_field_bytes() -> Vec<u8> {
4240 serialize(&StructSingleField {
4241 name: "facet".into(),
4242 })
4243 }
4244
4245 pub fn sequence_numbers_bytes() -> Vec<u8> {
4247 serialize(&vec![1u64, 2, 3])
4248 }
4249
4250 pub fn struct_nested_bytes() -> Vec<u8> {
4252 serialize(&NestedParent {
4253 id: 42,
4254 child: NestedChild {
4255 code: "alpha".into(),
4256 active: true,
4257 },
4258 tags: vec!["core".into(), "json".into()],
4259 })
4260 }
4261
4262 pub fn scalar_bool_bytes() -> Vec<u8> {
4264 serialize(&BoolWrapper {
4265 yes: true,
4266 no: false,
4267 })
4268 }
4269
4270 pub fn scalar_integers_bytes() -> Vec<u8> {
4272 serialize(&IntegerTypes {
4273 signed_8: -128,
4274 unsigned_8: 255,
4275 signed_32: -2_147_483_648,
4276 unsigned_32: 4_294_967_295,
4277 signed_64: -9_223_372_036_854_775_808,
4278 unsigned_64: 18_446_744_073_709_551_615,
4279 })
4280 }
4281
4282 pub fn scalar_floats_bytes() -> Vec<u8> {
4284 serialize(&FloatTypes {
4285 float_32: 1.5,
4286 float_64: 2.25,
4287 })
4288 }
4289
4290 pub fn map_string_keys_bytes() -> Vec<u8> {
4292 let mut map = std::collections::BTreeMap::new();
4293 map.insert("alpha".to_string(), 1);
4294 map.insert("beta".to_string(), 2);
4295 serialize(&MapWrapper { data: map })
4296 }
4297
4298 pub fn tuple_simple_bytes() -> Vec<u8> {
4300 serialize(&TupleWrapper {
4301 triple: ("hello".to_string(), 42, true),
4302 })
4303 }
4304
4305 pub fn tuple_nested_bytes() -> Vec<u8> {
4307 serialize(&NestedTupleWrapper {
4308 outer: ((1, 2), ("test".to_string(), true)),
4309 })
4310 }
4311
4312 pub fn option_none_bytes() -> Vec<u8> {
4314 serialize(&WithOption {
4315 name: "test".to_string(),
4316 nickname: None,
4317 })
4318 }
4319
4320 pub fn option_some_bytes() -> Vec<u8> {
4322 serialize(&WithOption {
4323 name: "test".to_string(),
4324 nickname: Some("nick".to_string()),
4325 })
4326 }
4327
4328 pub fn enum_unit_variant_bytes() -> Vec<u8> {
4330 serialize(&UnitVariantEnum::Active)
4331 }
4332
4333 pub fn vec_nested_bytes() -> Vec<u8> {
4335 serialize(&NestedVecWrapper {
4336 matrix: vec![vec![1, 2], vec![3, 4, 5]],
4337 })
4338 }
4339
4340 pub fn bytes_vec_u8_bytes() -> Vec<u8> {
4342 serialize(&BytesWrapper {
4343 data: vec![0xDE, 0xAD, 0xBE, 0xEF],
4344 })
4345 }
4346
4347 pub fn char_scalar_bytes() -> Vec<u8> {
4349 serialize(&CharWrapper {
4350 letter: 'A',
4351 emoji: '🦀',
4352 })
4353 }
4354
4355 pub fn unit_struct_bytes() -> Vec<u8> {
4357 serialize(&UnitStruct)
4358 }
4359
4360 pub fn hashset_bytes() -> Vec<u8> {
4362 let mut items = std::collections::HashSet::new();
4363 items.insert("alpha".to_string());
4364 items.insert("beta".to_string());
4365 items.insert("gamma".to_string());
4366 serialize(&HashSetWrapper { items })
4367 }
4368
4369 pub fn tuple_empty_bytes() -> Vec<u8> {
4371 serialize(&EmptyTupleWrapper {
4372 name: "test".to_string(),
4373 empty: (),
4374 })
4375 }
4376
4377 pub fn tuple_single_element_bytes() -> Vec<u8> {
4379 serialize(&SingleElementTupleWrapper {
4380 name: "test".to_string(),
4381 single: (42,),
4382 })
4383 }
4384
4385 pub fn enum_complex_bytes() -> Vec<u8> {
4387 serialize(&ComplexEnum::Label {
4388 name: "facet".into(),
4389 level: 7,
4390 })
4391 }
4392
4393 pub fn box_wrapper_bytes() -> Vec<u8> {
4395 serialize(&BoxWrapper {
4396 inner: Box::new(42),
4397 })
4398 }
4399
4400 pub fn arc_wrapper_bytes() -> Vec<u8> {
4402 serialize(&ArcWrapper {
4403 inner: std::sync::Arc::new(42),
4404 })
4405 }
4406
4407 pub fn rc_wrapper_bytes() -> Vec<u8> {
4409 serialize(&RcWrapper {
4410 inner: std::rc::Rc::new(42),
4411 })
4412 }
4413
4414 pub fn set_btree_bytes() -> Vec<u8> {
4416 let mut items = std::collections::BTreeSet::new();
4417 items.insert("alpha".to_string());
4418 items.insert("beta".to_string());
4419 items.insert("gamma".to_string());
4420 serialize(&SetWrapper { items })
4421 }
4422
4423 pub fn scalar_integers_16_bytes() -> Vec<u8> {
4425 serialize(&IntegerTypes16 {
4426 signed_16: -32768,
4427 unsigned_16: 65535,
4428 })
4429 }
4430
4431 pub fn scalar_integers_128_bytes() -> Vec<u8> {
4433 serialize(&IntegerTypes128 {
4434 signed_128: -170141183460469231731687303715884105728,
4435 unsigned_128: 340282366920938463463374607431768211455,
4436 })
4437 }
4438
4439 pub fn scalar_integers_size_bytes() -> Vec<u8> {
4441 serialize(&IntegerTypesSize {
4442 signed_size: -1000,
4443 unsigned_size: 2000,
4444 })
4445 }
4446
4447 pub fn nonzero_integers_bytes() -> Vec<u8> {
4449 serialize(&NonZeroTypes {
4450 nz_u32: std::num::NonZeroU32::new(42).unwrap(),
4451 nz_i64: std::num::NonZeroI64::new(-100).unwrap(),
4452 })
4453 }
4454
4455 pub fn cow_str_bytes() -> Vec<u8> {
4457 serialize(&CowStrWrapper {
4458 owned: std::borrow::Cow::Owned("hello world".to_string()),
4459 message: std::borrow::Cow::Borrowed("borrowed"),
4460 })
4461 }
4462
4463 pub fn array_fixed_size_bytes() -> Vec<u8> {
4465 serialize(&ArrayWrapper { values: [1, 2, 3] })
4466 }
4467
4468 pub fn skip_unknown_fields_bytes() -> Vec<u8> {
4471 #[derive(serde::Serialize)]
4472 struct WithExtra {
4473 known: String,
4474 extra: String,
4475 }
4476 serialize(&WithExtra {
4477 known: "value".to_string(),
4478 extra: "ignored".to_string(),
4479 })
4480 }
4481
4482 #[cfg(feature = "net")]
4489 fn net_addr_wrapper_bytes(addr_str: &str) -> Vec<u8> {
4490 use std::collections::BTreeMap;
4491 let mut map: BTreeMap<&str, &str> = BTreeMap::new();
4492 map.insert("addr", addr_str);
4493 serialize(&map)
4494 }
4495
4496 #[cfg(feature = "net")]
4498 pub fn net_ip_addr_v4_bytes() -> Vec<u8> {
4499 net_addr_wrapper_bytes("192.168.1.1")
4500 }
4501
4502 #[cfg(feature = "net")]
4504 pub fn net_ip_addr_v6_bytes() -> Vec<u8> {
4505 net_addr_wrapper_bytes("2001:db8::1")
4506 }
4507
4508 #[cfg(feature = "net")]
4510 pub fn net_ipv4_addr_bytes() -> Vec<u8> {
4511 net_addr_wrapper_bytes("127.0.0.1")
4512 }
4513
4514 #[cfg(feature = "net")]
4516 pub fn net_ipv6_addr_bytes() -> Vec<u8> {
4517 net_addr_wrapper_bytes("::1")
4518 }
4519
4520 #[cfg(feature = "net")]
4522 pub fn net_socket_addr_v4_bytes() -> Vec<u8> {
4523 net_addr_wrapper_bytes("192.168.1.1:8080")
4524 }
4525
4526 #[cfg(feature = "net")]
4528 pub fn net_socket_addr_v6_bytes() -> Vec<u8> {
4529 net_addr_wrapper_bytes("[2001:db8::1]:443")
4530 }
4531
4532 #[cfg(feature = "net")]
4534 pub fn net_socket_addr_v4_explicit_bytes() -> Vec<u8> {
4535 net_addr_wrapper_bytes("10.0.0.1:3000")
4536 }
4537
4538 #[cfg(feature = "net")]
4540 pub fn net_socket_addr_v6_explicit_bytes() -> Vec<u8> {
4541 net_addr_wrapper_bytes("[fe80::1]:9000")
4542 }
4543}
4544
4545fn emit_case_showcase_dynamic<S, T>(
4547 desc: &'static CaseDescriptor<T>,
4548 note: Option<&'static str>,
4549 roundtrip_disabled_reason: Option<&'static str>,
4550 input: &[u8],
4551 actual: &T,
4552) where
4553 S: FormatSuite,
4554 for<'facet> T: Facet<'facet>,
4555 T: Debug,
4556{
4557 let input_block = format_hex_dump(input);
4559
4560 let pretty_output = format!(
4561 "{}",
4562 actual.pretty_with(PrettyPrinter::new().with_indent_size(2))
4563 );
4564 let note_line = note.map(|n| format!("note: {n}\n")).unwrap_or_default();
4565 let roundtrip_line = roundtrip_disabled_reason
4566 .map(|r| format!("roundtrip: disabled ({r})\n"))
4567 .unwrap_or_default();
4568
4569 println!(
4570 "{}",
4571 formatdoc!(
4572 "
4573 ── facet-format-suite :: {format_name} :: {case_id} ──
4574 description: {description}
4575 {note_line}{roundtrip_line}Input (binary, {len} bytes):
4576 {input_block}
4577
4578 facet-pretty output:
4579 {pretty_output}
4580 ",
4581 format_name = S::format_name(),
4582 case_id = desc.id,
4583 description = desc.description,
4584 note_line = note_line,
4585 roundtrip_line = roundtrip_line,
4586 len = input.len(),
4587 input_block = input_block,
4588 pretty_output = pretty_output,
4589 )
4590 );
4591}
4592
4593fn format_hex_dump(input: &[u8]) -> String {
4595 use std::fmt::Write;
4596 let mut output = String::new();
4597 for (i, chunk) in input.chunks(16).enumerate() {
4598 write!(output, "{:08x} ", i * 16).unwrap();
4600 for (j, byte) in chunk.iter().enumerate() {
4602 if j == 8 {
4603 output.push(' ');
4604 }
4605 write!(output, "{:02x} ", byte).unwrap();
4606 }
4607 for j in chunk.len()..16 {
4609 if j == 8 {
4610 output.push(' ');
4611 }
4612 output.push_str(" ");
4613 }
4614 output.push_str(" |");
4616 for byte in chunk {
4617 if byte.is_ascii_graphic() || *byte == b' ' {
4618 output.push(*byte as char);
4619 } else {
4620 output.push('.');
4621 }
4622 }
4623 output.push_str("|\n");
4624 }
4625 output
4626}