1use alloc::vec::Vec;
25use core::any::TypeId;
26use core::fmt::{Display, Formatter, Write};
27
28#[cfg(feature = "functions")]
29use crate::func::Function;
30use crate::{
31 array::Array,
32 enums::{Enum, VariantType},
33 list::List,
34 map::Map,
35 set::Set,
36 structs::Struct,
37 tuple::Tuple,
38 tuple_struct::TupleStruct,
39 PartialReflect, Reflect, ReflectRef, TypeInfo,
40};
41
42fn write_value(
55 f: &mut Formatter<'_>,
56 value: &dyn PartialReflect,
57 ancestry: &mut Vec<TypeId>,
61 indent: u32,
62) -> core::fmt::Result {
63 let Some(type_info) = value.get_represented_type_info() else {
65 return write_reflect_ref(f, &value.reflect_ref(), ancestry, indent);
66 };
67
68 let type_id = type_info.type_id();
69 let type_path = type_info.type_path();
70
71 if ancestry.contains(&type_id) {
72 return write!(f, "{type_path} {{ ... }}");
73 }
74
75 ancestry.push(type_id);
76 let result = write_reflect_ref(f, &value.reflect_ref(), ancestry, indent);
77 ancestry.pop();
78 result
79}
80
81fn write_reflect_ref(
83 f: &mut Formatter<'_>,
84 reflect_ref: &ReflectRef<'_>,
85 ancestry: &mut Vec<TypeId>,
86 indent: u32,
87) -> core::fmt::Result {
88 match *reflect_ref {
89 ReflectRef::Struct(value) => write_struct(f, value, ancestry, indent),
90 ReflectRef::TupleStruct(value) => write_tuple_struct(f, value, ancestry, indent),
91 ReflectRef::Tuple(value) => write_tuple(f, value, ancestry, indent),
92 ReflectRef::List(value) => write_list(f, value, ancestry, indent),
93 ReflectRef::Array(value) => write_array(f, value, ancestry, indent),
94 ReflectRef::Map(value) => write_map(f, value, ancestry, indent),
95 ReflectRef::Set(value) => write_set(f, value, ancestry, indent),
96 ReflectRef::Enum(value) => write_enum(f, value, ancestry, indent),
97 ReflectRef::Opaque(value) => write_opaque(f, value, indent),
98 #[cfg(feature = "functions")]
99 ReflectRef::Function(function) => write_function(f, function),
100 }
101}
102
103fn write_struct(
105 f: &mut Formatter<'_>,
106 value: &dyn Struct,
107 ancestry: &mut Vec<TypeId>,
108 indent: u32,
109) -> core::fmt::Result {
110 let type_name = display_type_name(value.get_represented_type_info(), "<Unknown Struct>");
111
112 if value.field_len() == 0 {
113 return write!(f, "{type_name} {{}}");
114 }
115
116 write_delimited_block(f, type_name, " ", '{', '}', indent, |fmt, entry_indent| {
117 for i in 0..value.field_len() {
118 let field_name = value.name_at(i).unwrap_or("<Unknown Field>");
119 write_indent(fmt, entry_indent)?;
120 fmt.write_str(field_name)?;
121 fmt.write_str(": ")?;
122 match value.field_at(i) {
123 Some(child) => write_value(fmt, child, ancestry, entry_indent)?,
124 None => fmt.write_str("<None>")?,
125 }
126 fmt.write_str(",\n")?;
127 }
128 Ok(())
129 })
130}
131
132fn write_tuple_struct(
134 f: &mut Formatter<'_>,
135 value: &dyn TupleStruct,
136 ancestry: &mut Vec<TypeId>,
137 indent: u32,
138) -> core::fmt::Result {
139 let type_name = display_type_name(value.get_represented_type_info(), "<Unknown TupleStruct>");
140
141 if value.field_len() == 0 {
142 return write!(f, "{type_name}()");
143 }
144
145 write_delimited_block(f, type_name, "", '(', ')', indent, |fmt, entry_indent| {
146 for i in 0..value.field_len() {
147 write_indent(fmt, entry_indent)?;
148 match value.field(i) {
149 Some(child) => write_value(fmt, child, ancestry, entry_indent)?,
150 None => fmt.write_str("<None>")?,
151 }
152 fmt.write_str(",\n")?;
153 }
154 Ok(())
155 })
156}
157
158fn write_tuple(
160 f: &mut Formatter<'_>,
161 value: &dyn Tuple,
162 ancestry: &mut Vec<TypeId>,
163 indent: u32,
164) -> core::fmt::Result {
165 if value.field_len() == 0 {
166 return f.write_str("()");
167 }
168
169 write_delimited_block(f, "", "", '(', ')', indent, |fmt, entry_indent| {
170 for i in 0..value.field_len() {
171 write_indent(fmt, entry_indent)?;
172 match value.field(i) {
173 Some(child) => write_value(fmt, child, ancestry, entry_indent)?,
174 None => fmt.write_str("<None>")?,
175 }
176 fmt.write_str(",\n")?;
177 }
178 Ok(())
179 })
180}
181
182fn write_list(
184 f: &mut Formatter<'_>,
185 value: &dyn List,
186 ancestry: &mut Vec<TypeId>,
187 indent: u32,
188) -> core::fmt::Result {
189 if value.is_empty() {
190 return f.write_str("[]");
191 }
192
193 write_delimited_block(f, "", "", '[', ']', indent, |fmt, entry_indent| {
194 for i in 0..value.len() {
195 write_indent(fmt, entry_indent)?;
196 match value.get(i) {
197 Some(child) => write_value(fmt, child, ancestry, entry_indent)?,
198 None => fmt.write_str("<None>")?,
199 }
200 fmt.write_str(",\n")?;
201 }
202 Ok(())
203 })
204}
205
206fn write_array(
208 f: &mut Formatter<'_>,
209 value: &dyn Array,
210 ancestry: &mut Vec<TypeId>,
211 indent: u32,
212) -> core::fmt::Result {
213 if value.is_empty() {
214 return f.write_str("[]");
215 }
216
217 write_delimited_block(f, "", "", '[', ']', indent, |fmt, entry_indent| {
218 for i in 0..value.len() {
219 write_indent(fmt, entry_indent)?;
220 match value.get(i) {
221 Some(child) => write_value(fmt, child, ancestry, entry_indent)?,
222 None => fmt.write_str("<None>")?,
223 }
224 fmt.write_str(",\n")?;
225 }
226 Ok(())
227 })
228}
229
230fn write_map(
232 f: &mut Formatter<'_>,
233 value: &dyn Map,
234 ancestry: &mut Vec<TypeId>,
235 indent: u32,
236) -> core::fmt::Result {
237 if value.is_empty() {
238 return f.write_str("{}");
239 }
240
241 write_delimited_block(f, "", "", '{', '}', indent, |fmt, entry_indent| {
242 for (key, key_value) in value.iter() {
243 write_indent(fmt, entry_indent)?;
244 write_value(fmt, key, ancestry, entry_indent)?;
245 fmt.write_str(": ")?;
246 write_value(fmt, key_value, ancestry, entry_indent)?;
247 fmt.write_str(",\n")?;
248 }
249 Ok(())
250 })
251}
252
253fn write_set(
255 f: &mut Formatter<'_>,
256 value: &dyn Set,
257 ancestry: &mut Vec<TypeId>,
258 indent: u32,
259) -> core::fmt::Result {
260 if value.is_empty() {
261 return f.write_str("{}");
262 }
263
264 write_delimited_block(f, "", "", '{', '}', indent, |fmt, entry_indent| {
265 for element in value.iter() {
266 write_indent(fmt, entry_indent)?;
267 write_value(fmt, element, ancestry, entry_indent)?;
268 fmt.write_str(",\n")?;
269 }
270 Ok(())
271 })
272}
273
274fn write_enum(
276 f: &mut Formatter<'_>,
277 value: &dyn Enum,
278 ancestry: &mut Vec<TypeId>,
279 indent: u32,
280) -> core::fmt::Result {
281 let type_name = display_type_name(value.get_represented_type_info(), "<Unknown Enum>");
282 let variant = value.variant_name();
283 let variant = if variant.is_empty() {
284 "<unnamed>"
285 } else {
286 variant
287 };
288 let qualified = alloc::format!("{type_name}::{variant}");
289
290 match value.variant_type() {
291 VariantType::Struct => {
292 if value.field_len() == 0 {
293 return write!(f, "{qualified} {{}}");
294 }
295
296 write_delimited_block(f, &qualified, " ", '{', '}', indent, |fmt, entry_indent| {
297 for i in 0..value.field_len() {
298 let field_name = value.name_at(i).unwrap_or("<Unknown Field>");
299 write_indent(fmt, entry_indent)?;
300 fmt.write_str(field_name)?;
301 fmt.write_str(": ")?;
302 match value.field_at(i) {
303 Some(child) => write_value(fmt, child, ancestry, entry_indent)?,
304 None => fmt.write_str("<None>")?,
305 }
306 fmt.write_str(",\n")?;
307 }
308 Ok(())
309 })
310 }
311 VariantType::Tuple => {
312 if value.field_len() == 0 {
313 return write!(f, "{qualified}()");
314 }
315
316 write_delimited_block(f, &qualified, "", '(', ')', indent, |fmt, entry_indent| {
317 for i in 0..value.field_len() {
318 write_indent(fmt, entry_indent)?;
319 match value.field_at(i) {
320 Some(child) => write_value(fmt, child, ancestry, entry_indent)?,
321 None => fmt.write_str("<None>")?,
322 }
323 fmt.write_str(",\n")?;
324 }
325 Ok(())
326 })
327 }
328 VariantType::Unit => write!(f, "{qualified}"),
329 }
330}
331
332#[cfg(feature = "functions")]
336fn write_function(f: &mut Formatter<'_>, func: &dyn Function) -> core::fmt::Result {
337 let pretty = func
338 .info()
339 .pretty_printer()
340 .include_fn_token()
341 .include_name();
342 write!(f, "{pretty:?}")
345}
346
347impl Display for dyn Struct {
348 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
349 write_struct(f, self, &mut Vec::new(), 0)
350 }
351}
352
353impl Display for dyn TupleStruct {
354 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
355 write_tuple_struct(f, self, &mut Vec::new(), 0)
356 }
357}
358
359impl Display for dyn Tuple {
360 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
361 write_tuple(f, self, &mut Vec::new(), 0)
362 }
363}
364
365impl Display for dyn List {
366 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
367 write_list(f, self, &mut Vec::new(), 0)
368 }
369}
370
371impl Display for dyn Array {
372 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
373 write_array(f, self, &mut Vec::new(), 0)
374 }
375}
376
377impl Display for dyn Map {
378 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
379 write_map(f, self, &mut Vec::new(), 0)
380 }
381}
382
383impl Display for dyn Set {
384 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
385 write_set(f, self, &mut Vec::new(), 0)
386 }
387}
388
389impl Display for dyn Enum {
390 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
391 write_enum(f, self, &mut Vec::new(), 0)
392 }
393}
394
395#[cfg(feature = "functions")]
396impl Display for dyn Function {
397 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
398 write_function(f, self)
399 }
400}
401
402impl Display for ReflectRef<'_> {
403 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
404 let mut ancestry = Vec::new();
405 write_reflect_ref(f, self, &mut ancestry, 0)
406 }
407}
408
409impl Display for dyn PartialReflect {
410 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
411 write_value(f, self, &mut Vec::new(), 0)
412 }
413}
414
415impl Display for dyn Reflect {
416 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
417 Display::fmt(self as &dyn PartialReflect, f)
418 }
419}
420
421fn display_type_name(type_info: Option<&TypeInfo>, fallback: &'static str) -> &'static str {
425 match type_info {
426 Some(info) => info.type_path(),
427 None => fallback,
428 }
429}
430
431fn write_indent(f: &mut Formatter<'_>, level: u32) -> core::fmt::Result {
433 for _ in 0..level {
434 f.write_str(" ")?;
435 }
436 Ok(())
437}
438
439fn write_delimited_block(
448 f: &mut Formatter<'_>,
449 prefix: &str,
450 separator: &str,
451 open: char,
452 close: char,
453 indent: u32,
454 write_entries: impl FnOnce(&mut Formatter<'_>, u32) -> core::fmt::Result,
455) -> core::fmt::Result {
456 let entry_indent = indent + 1;
457 writeln!(f, "{prefix}{separator}{open}")?;
458 write_entries(f, entry_indent)?;
459 write_indent(f, indent)?;
460 f.write_char(close)
461}
462
463fn write_opaque(
472 f: &mut Formatter<'_>,
473 value: &dyn PartialReflect,
474 indent: u32,
475) -> core::fmt::Result {
476 let debug = alloc::format!("{value:?}");
477 let trimmed = debug.trim();
478
479 if trimmed.len() == debug.len() && !trimmed.contains('\n') {
481 return f.write_str(&debug);
482 }
483
484 for (i, line) in trimmed.lines().enumerate() {
487 if i > 0 {
488 f.write_char('\n')?;
489 }
490 if line.trim().is_empty() {
491 continue;
492 }
493 if i > 0 {
494 write_indent(f, indent + 1)?;
495 }
496 f.write_str(line)?;
498 }
499 Ok(())
500}
501
502#[cfg(test)]
503mod tests {
504 use super::*;
505 use crate::{
506 array::DynamicArray, map::DynamicMap, set::DynamicSet, structs::DynamicStruct,
507 tuple::DynamicTuple, tuple_struct::DynamicTupleStruct, Reflect,
508 };
509 use alloc::{
510 boxed::Box,
511 collections::BTreeMap,
512 format,
513 string::{String, ToString},
514 vec,
515 };
516 use bevy_platform::collections::HashSet;
517 use disqualified::ShortName;
518
519 #[derive(Reflect, PartialEq, Eq, Hash)]
520 #[reflect(Hash)]
521 struct Inner {
522 a: u32,
523 b: String,
524 }
525
526 #[derive(Reflect)]
527 struct Outer {
528 name: String,
529 inner: Inner,
530 list: Vec<i32>,
531 }
532
533 #[derive(Reflect)]
534 struct GraphNode {
535 value: u32,
536 children: Vec<GraphNode>,
537 }
538
539 #[derive(Reflect)]
540 struct EmptyStruct {}
541
542 #[derive(Reflect)]
543 struct MutuallyRecursiveA {
544 items: Vec<MutuallyRecursiveB>,
545 }
546
547 #[derive(Reflect)]
548 struct MutuallyRecursiveB {
549 items: Vec<MutuallyRecursiveA>,
550 }
551
552 #[derive(Reflect)]
553 struct Newtype(u32);
554
555 #[derive(Reflect)]
556 struct EmptyTupleStruct();
557
558 #[derive(Reflect)]
559 enum MyEnum {
560 Unit,
561 Tuple(u32, String),
562 Struct { x: i32, inner: Inner },
563 EmptyTuple(),
564 EmptyStruct {},
565 }
566
567 #[test]
568 fn opaque_values_use_debug() {
569 assert_eq!(format!("{}", &42u32 as &dyn PartialReflect), "42");
570 assert_eq!(
571 format!("{}", &"hi".to_string() as &dyn PartialReflect),
572 "\"hi\""
573 );
574 assert_eq!(format!("{}", &true as &dyn PartialReflect), "true");
575 }
576
577 #[test]
578 fn flat_struct() {
579 let value = Inner {
580 a: 1,
581 b: "two".to_string(),
582 };
583 assert_eq!(
584 format!("{}", &value as &dyn PartialReflect),
585 "bevy_reflect::display::tests::Inner {\n a: 1,\n b: \"two\",\n}"
586 );
587 }
588
589 #[test]
590 fn empty_containers_collapse_to_one_line() {
591 assert_eq!(
592 format!("{}", &EmptyStruct {} as &dyn PartialReflect),
593 "bevy_reflect::display::tests::EmptyStruct {}"
594 );
595 assert_eq!(
596 format!("{}", &EmptyTupleStruct() as &dyn PartialReflect),
597 "bevy_reflect::display::tests::EmptyTupleStruct()"
598 );
599 assert_eq!(
600 format!("{}", &Vec::<i32>::new() as &dyn PartialReflect),
601 "[]"
602 );
603 assert_eq!(format!("{}", &[0i32; 0] as &dyn PartialReflect), "[]");
604 assert_eq!(format!("{}", &() as &dyn PartialReflect), "()");
605 assert_eq!(
606 format!("{}", &BTreeMap::<u32, u32>::new() as &dyn PartialReflect),
607 "{}"
608 );
609 assert_eq!(
610 format!("{}", &HashSet::<u32>::new() as &dyn PartialReflect),
611 "{}"
612 );
613 }
614
615 #[test]
616 fn empty_enum_variants() {
617 assert_eq!(
618 format!("{}", &MyEnum::EmptyTuple() as &dyn PartialReflect),
619 "bevy_reflect::display::tests::MyEnum::EmptyTuple()"
620 );
621 assert_eq!(
622 format!("{}", &MyEnum::EmptyStruct {} as &dyn PartialReflect),
623 "bevy_reflect::display::tests::MyEnum::EmptyStruct {}"
624 );
625 }
626
627 #[test]
628 fn newtype_struct() {
629 assert_eq!(
630 format!("{}", &Newtype(5) as &dyn PartialReflect),
631 "bevy_reflect::display::tests::Newtype(\n 5,\n)"
632 );
633 }
634
635 #[test]
636 fn list_of_scalars() {
637 assert_eq!(
638 format!("{}", &vec![1, 2, 3] as &dyn PartialReflect),
639 "[\n 1,\n 2,\n 3,\n]"
640 );
641 }
642
643 #[test]
644 fn map_entries() {
645 let mut map = BTreeMap::new();
646 map.insert(1u32, "one".to_string());
647 map.insert(2u32, "two".to_string());
648 assert_eq!(
649 format!("{}", &map as &dyn PartialReflect),
650 "{\n 1: \"one\",\n 2: \"two\",\n}"
651 );
652 }
653
654 #[test]
655 fn enum_variants() {
656 assert_eq!(
657 format!("{}", &MyEnum::Unit as &dyn PartialReflect),
658 "bevy_reflect::display::tests::MyEnum::Unit"
659 );
660 assert_eq!(
661 format!(
662 "{}",
663 &MyEnum::Tuple(7, "t".to_string()) as &dyn PartialReflect
664 ),
665 "bevy_reflect::display::tests::MyEnum::Tuple(\n 7,\n \"t\",\n)"
666 );
667 }
668
669 #[test]
670 fn nested_values_are_indented_per_level() {
671 let value = Outer {
672 name: "hello".to_string(),
673 inner: Inner {
674 a: 1,
675 b: "two".to_string(),
676 },
677 list: vec![10, 20],
678 };
679
680 let expected = "\
681bevy_reflect::display::tests::Outer {
682 name: \"hello\",
683 inner: bevy_reflect::display::tests::Inner {
684 a: 1,
685 b: \"two\",
686 },
687 list: [
688 10,
689 20,
690 ],
691}";
692 assert_eq!(format!("{}", &value as &dyn PartialReflect), expected);
693 }
694
695 #[test]
696 fn deeply_nested_enum_struct_variant() {
697 let value = MyEnum::Struct {
698 x: -1,
699 inner: Inner {
700 a: 2,
701 b: "q".to_string(),
702 },
703 };
704
705 let expected = "\
706bevy_reflect::display::tests::MyEnum::Struct {
707 x: -1,
708 inner: bevy_reflect::display::tests::Inner {
709 a: 2,
710 b: \"q\",
711 },
712}";
713 assert_eq!(format!("{}", &value as &dyn PartialReflect), expected);
714 }
715
716 #[test]
717 fn type_name_uses_full_path() {
718 let value = Inner {
719 a: 1,
720 b: "two".to_string(),
721 };
722 assert_eq!(
723 format!("{}", &value as &dyn PartialReflect),
724 "bevy_reflect::display::tests::Inner {\n a: 1,\n b: \"two\",\n}"
725 );
726 }
727
728 #[test]
729 fn shortname_collapses_type_paths() {
730 let value = Outer {
731 name: "hello".to_string(),
732 inner: Inner {
733 a: 1,
734 b: "two".to_string(),
735 },
736 list: vec![10, 20],
737 };
738
739 let full = format!("{}", &value as &dyn PartialReflect);
740 assert!(full.contains("bevy_reflect::display::tests::Outer"));
742 assert!(full.contains("bevy_reflect::display::tests::Inner"));
743
744 let short = ShortName::from(full.as_str()).to_string();
745 assert_eq!(
746 short,
747 concat!(
748 "Outer {\n",
749 " name: \"hello\",\n",
750 " inner: Inner {\n",
751 " a: 1,\n",
752 " b: \"two\",\n",
753 " },\n",
754 " list: [\n",
755 " 10,\n",
756 " 20,\n",
757 " ],\n",
758 "}"
759 )
760 );
761 }
762
763 #[derive(Reflect, Clone)]
764 #[reflect(opaque, Debug)]
765 struct TrailingNewlineDebug;
766
767 impl core::fmt::Debug for TrailingNewlineDebug {
768 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
769 writeln!(f, "value")
770 }
771 }
772
773 #[derive(Reflect)]
774 struct HoldsTrailingNewline {
775 op: TrailingNewlineDebug,
776 }
777
778 #[test]
779 fn opaque_debug_trailing_newline_does_not_orphan_comma() {
780 assert_eq!(
781 format!("{}", &TrailingNewlineDebug as &dyn PartialReflect),
782 "value"
783 );
784 assert_eq!(
785 format!(
786 "{}",
787 &HoldsTrailingNewline {
788 op: TrailingNewlineDebug
789 } as &dyn PartialReflect
790 ),
791 "bevy_reflect::display::tests::HoldsTrailingNewline {\n op: value,\n}"
792 );
793 }
794
795 #[derive(Reflect, Clone)]
796 #[reflect(opaque, Debug)]
797 struct TrailingNewlineThenSpaces;
798
799 impl core::fmt::Debug for TrailingNewlineThenSpaces {
800 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
801 write!(f, "value\n ")
802 }
803 }
804
805 #[derive(Reflect)]
806 struct HoldsTrailingNewlineThenSpaces {
807 op: TrailingNewlineThenSpaces,
808 }
809
810 #[test]
811 fn opaque_debug_trailing_whitespace_does_not_orphan_comma() {
812 assert_eq!(
813 format!("{}", &TrailingNewlineThenSpaces as &dyn PartialReflect),
814 "value"
815 );
816 assert_eq!(
817 format!(
818 "{}",
819 &HoldsTrailingNewlineThenSpaces {
820 op: TrailingNewlineThenSpaces
821 } as &dyn PartialReflect
822 ),
823 "bevy_reflect::display::tests::HoldsTrailingNewlineThenSpaces {\n op: value,\n}"
824 );
825 }
826
827 #[derive(Reflect, Clone)]
828 #[reflect(opaque, Debug)]
829 struct MultiLineDebug;
830
831 impl core::fmt::Debug for MultiLineDebug {
832 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
833 write!(f, "line1\nline2")
834 }
835 }
836
837 #[derive(Reflect)]
838 struct HoldsMultiLine {
839 op: MultiLineDebug,
840 }
841
842 #[test]
843 fn multi_line_opaque_value_indents_continuation_lines() {
844 assert_eq!(
845 format!("{}", &MultiLineDebug as &dyn PartialReflect),
846 "line1\n line2"
847 );
848 assert_eq!(
849 format!(
850 "{}",
851 &HoldsMultiLine { op: MultiLineDebug } as &dyn PartialReflect
852 ),
853 "bevy_reflect::display::tests::HoldsMultiLine {\n op: line1\n line2,\n}"
854 );
855 }
856
857 #[derive(Reflect, Clone)]
858 #[reflect(opaque, Debug)]
859 struct BlankInteriorLineDebug;
860
861 impl core::fmt::Debug for BlankInteriorLineDebug {
862 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
863 write!(f, "line1\n \nline2")
864 }
865 }
866
867 #[derive(Reflect)]
868 struct HoldsBlankInterior {
869 op: BlankInteriorLineDebug,
870 }
871
872 #[test]
873 fn opaque_debug_blank_interior_line_carries_no_trailing_whitespace() {
874 assert_eq!(
875 format!("{}", &BlankInteriorLineDebug as &dyn PartialReflect),
876 "line1\n\n line2"
877 );
878 assert_eq!(
879 format!(
880 "{}",
881 &HoldsBlankInterior {
882 op: BlankInteriorLineDebug
883 } as &dyn PartialReflect
884 ),
885 "bevy_reflect::display::tests::HoldsBlankInterior {\n op: line1\n\n line2,\n}"
886 );
887 let formatted = format!(
888 "{}",
889 &HoldsBlankInterior {
890 op: BlankInteriorLineDebug
891 } as &dyn PartialReflect
892 );
893 for line in formatted.lines() {
894 assert_eq!(
895 line.trim_end(),
896 line,
897 "line has trailing whitespace: {line:?}"
898 );
899 }
900 }
901
902 #[derive(Reflect, Clone)]
903 #[reflect(opaque, Debug)]
904 struct LeadingNewlineDebug;
905
906 impl core::fmt::Debug for LeadingNewlineDebug {
907 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
908 write!(f, "\nvalue")
909 }
910 }
911
912 #[derive(Reflect)]
913 struct HoldsLeadingNewline {
914 op: LeadingNewlineDebug,
915 }
916
917 #[test]
918 fn opaque_debug_leading_newline_does_not_leave_trailing_whitespace() {
919 assert_eq!(
920 format!("{}", &LeadingNewlineDebug as &dyn PartialReflect),
921 "value"
922 );
923 assert_eq!(
924 format!(
925 "{}",
926 &HoldsLeadingNewline {
927 op: LeadingNewlineDebug
928 } as &dyn PartialReflect
929 ),
930 "bevy_reflect::display::tests::HoldsLeadingNewline {\n op: value,\n}"
931 );
932 }
933
934 #[test]
935 fn multi_line_struct_as_list_element() {
936 let value = vec![
937 Inner {
938 a: 1,
939 b: "x".to_string(),
940 },
941 Inner {
942 a: 2,
943 b: "y".to_string(),
944 },
945 ];
946 let expected = "\
947[
948 bevy_reflect::display::tests::Inner {
949 a: 1,
950 b: \"x\",
951 },
952 bevy_reflect::display::tests::Inner {
953 a: 2,
954 b: \"y\",
955 },
956]";
957 assert_eq!(format!("{}", &value as &dyn PartialReflect), expected);
958 }
959
960 #[test]
961 fn multi_line_struct_as_map_value() {
962 let mut map = BTreeMap::new();
963 map.insert(
964 1u32,
965 Inner {
966 a: 10,
967 b: "x".to_string(),
968 },
969 );
970 let expected = "\
971{
972 1: bevy_reflect::display::tests::Inner {
973 a: 10,
974 b: \"x\",
975 },
976}";
977 assert_eq!(format!("{}", &map as &dyn PartialReflect), expected);
978 }
979
980 #[test]
981 fn map_with_struct_key() {
982 let mut map = DynamicMap::default();
983 map.insert(
984 Inner {
985 a: 1,
986 b: "key".to_string(),
987 },
988 "value".to_string(),
989 );
990 assert_eq!(
991 format!("{}", &map as &dyn PartialReflect),
992 "{\n bevy_reflect::display::tests::Inner {\n a: 1,\n b: \"key\",\n }: \"value\",\n}"
993 );
994 }
995
996 #[test]
997 fn array_of_scalars() {
998 let array = DynamicArray::new(
999 vec![
1000 Box::new(1u32) as Box<dyn PartialReflect>,
1001 Box::new(2u32),
1002 Box::new(3u32),
1003 ]
1004 .into_boxed_slice(),
1005 );
1006 assert_eq!(
1007 format!("{}", &array as &dyn PartialReflect),
1008 "[\n 1,\n 2,\n 3,\n]"
1009 );
1010 }
1011
1012 #[test]
1013 fn set_with_one_element() {
1014 let mut set = DynamicSet::default();
1015 set.insert(42u32);
1016 assert_eq!(format!("{}", &set as &dyn PartialReflect), "{\n 42,\n}");
1017 }
1018
1019 #[test]
1020 fn set_with_struct_element() {
1021 let mut set = DynamicSet::default();
1022 set.insert(Inner {
1023 a: 10,
1024 b: "x".to_string(),
1025 });
1026 assert_eq!(
1027 format!("{}", &set as &dyn PartialReflect),
1028 "{\n bevy_reflect::display::tests::Inner {\n a: 10,\n b: \"x\",\n },\n}"
1029 );
1030 }
1031
1032 #[test]
1033 fn dynamic_struct_fallback_name() {
1034 let mut dyn_struct = DynamicStruct::default();
1035 dyn_struct.insert("x", 1u32);
1036 dyn_struct.insert("y", 2u32);
1037 assert_eq!(
1038 format!("{}", &dyn_struct as &dyn PartialReflect),
1039 "<Unknown Struct> {\n x: 1,\n y: 2,\n}"
1040 );
1041 }
1042
1043 #[test]
1044 fn dynamic_tuple_displays() {
1045 let mut dyn_tuple = DynamicTuple::default();
1046 dyn_tuple.insert(42u32);
1047 dyn_tuple.insert("hi".to_string());
1048 assert_eq!(
1049 format!("{}", &dyn_tuple as &dyn PartialReflect),
1050 "(\n 42,\n \"hi\",\n)"
1051 );
1052 }
1053
1054 #[test]
1055 fn dynamic_tuple_struct_fallback_name() {
1056 let mut dyn_tuple_struct = DynamicTupleStruct::default();
1057 dyn_tuple_struct.insert(42u32);
1058 dyn_tuple_struct.insert("hi".to_string());
1059 assert_eq!(
1060 format!("{}", &dyn_tuple_struct as &dyn PartialReflect),
1061 "<Unknown TupleStruct>(\n 42,\n \"hi\",\n)"
1062 );
1063 }
1064
1065 #[test]
1066 fn reflect_ref_displays() {
1067 let value = Inner {
1068 a: 1,
1069 b: "two".to_string(),
1070 };
1071 let reflect_ref = value.reflect_ref();
1072 assert_eq!(
1073 format!("{reflect_ref}"),
1074 "bevy_reflect::display::tests::Inner {\n a: 1,\n b: \"two\",\n}"
1075 );
1076 }
1077
1078 #[test]
1079 fn cycle_detection_truncates_on_repeated_type_path() {
1080 let child = GraphNode {
1081 value: 2,
1082 children: Vec::new(),
1083 };
1084 let parent = GraphNode {
1085 value: 1,
1086 children: vec![child],
1087 };
1088 assert_eq!(
1089 format!("{}", &parent as &dyn PartialReflect),
1090 concat!(
1091 "bevy_reflect::display::tests::GraphNode {\n",
1092 " value: 1,\n",
1093 " children: [\n",
1094 " bevy_reflect::display::tests::GraphNode { ... },\n",
1095 " ],\n",
1096 "}"
1097 )
1098 );
1099 }
1100
1101 #[test]
1102 fn cross_type_cycle_detection_truncates() {
1103 let inner = MutuallyRecursiveA { items: Vec::new() };
1104 let b = MutuallyRecursiveB { items: vec![inner] };
1105 let outer = MutuallyRecursiveA { items: vec![b] };
1106 assert_eq!(
1107 format!("{}", &outer as &dyn PartialReflect),
1108 concat!(
1109 "bevy_reflect::display::tests::MutuallyRecursiveA {\n",
1110 " items: [\n",
1111 " bevy_reflect::display::tests::MutuallyRecursiveB {\n",
1112 " items: [\n",
1113 " bevy_reflect::display::tests::MutuallyRecursiveA { ... },\n",
1114 " ],\n",
1115 " },\n",
1116 " ],\n",
1117 "}"
1118 )
1119 );
1120 }
1121
1122 #[cfg(feature = "functions")]
1123 mod functions {
1124 use super::*;
1125 use crate::func::IntoFunction;
1126
1127 #[test]
1128 fn named_function() {
1129 fn greet(name: &String) -> String {
1130 format!("Hello, {name}!")
1131 }
1132 let function = greet.into_function();
1133 assert_eq!(
1134 format!("{}", &function as &dyn PartialReflect),
1135 "fn bevy_reflect::display::tests::functions::named_function::greet(_: &alloc::string::String) -> alloc::string::String"
1136 );
1137 }
1138
1139 #[test]
1140 fn anonymous_function() {
1141 let function = (|a: i32, b: i32| a + b).into_function();
1142 assert_eq!(
1143 format!("{}", &function as &dyn PartialReflect),
1144 "fn _(_: i32, _: i32) -> i32"
1145 );
1146 }
1147
1148 #[test]
1149 fn overloaded_function() {
1150 fn add_i32(a: i32, b: i32) -> i32 {
1151 a + b
1152 }
1153 fn add_f32(a: f32, b: f32) -> f32 {
1154 a + b
1155 }
1156 let function = add_i32
1157 .into_function()
1158 .with_overload(add_f32)
1159 .with_name("add");
1160 assert_eq!(
1161 format!("{}", &function as &dyn PartialReflect),
1162 "fn add {(_: i32, _: i32) -> i32, (_: f32, _: f32) -> f32}"
1163 );
1164 }
1165 }
1166}