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