sbor/representations/display/
rustlike_string.rs

1use super::*;
2use crate::representations::*;
3use crate::rust::prelude::*;
4use crate::traversal::*;
5use crate::*;
6use TypedTraversalEvent::*;
7// TODO - This file could do with a minor refactor to commonize logic to print fields
8
9#[derive(Debug, Clone, Copy)]
10pub struct RustLikeDisplayContext<'s, 'a, E: FormattableCustomExtension> {
11    pub schema: &'s Schema<E::CustomSchema>,
12    pub custom_context: E::CustomDisplayContext<'a>,
13    pub print_mode: PrintMode,
14    pub options: RustLikeOptions,
15}
16
17#[derive(Debug, Clone, Copy)]
18pub struct RustLikeOptions {
19    pub include_enum_type_names: bool,
20    pub include_full_value_information: bool,
21}
22
23impl RustLikeOptions {
24    pub fn full() -> Self {
25        Self {
26            include_enum_type_names: true,
27            include_full_value_information: true,
28        }
29    }
30
31    pub fn debug_like() -> Self {
32        Self {
33            include_enum_type_names: false,
34            include_full_value_information: false,
35        }
36    }
37}
38
39impl Default for RustLikeOptions {
40    fn default() -> Self {
41        Self::full()
42    }
43}
44
45pub fn format_payload_as_rustlike_value<F: fmt::Write, E: FormattableCustomExtension>(
46    f: &mut F,
47    context: &RustLikeDisplayContext<'_, '_, E>,
48    payload: &'_ [u8],
49    type_id: LocalTypeId,
50    depth_limit: usize,
51) -> Result<(), FormattingError> {
52    let mut traverser = traverse_payload_with_types(payload, context.schema, type_id, depth_limit);
53    if let PrintMode::MultiLine {
54        first_line_indent, ..
55    } = &context.print_mode
56    {
57        write!(f, "{:first_line_indent$}", "")?;
58    }
59    format_value_tree(f, &mut traverser, context)?;
60    consume_end_event(&mut traverser)?;
61    Ok(())
62}
63
64#[allow(clippy::too_many_arguments)]
65pub(crate) fn format_partial_payload_as_rustlike_value<
66    F: fmt::Write,
67    E: FormattableCustomExtension,
68>(
69    f: &mut F,
70    partial_payload: &[u8],
71    expected_start: ExpectedStart<E::CustomValueKind>,
72    check_exact_end: bool,
73    current_depth: usize,
74    context: &RustLikeDisplayContext<'_, '_, E>,
75    type_id: LocalTypeId,
76    depth_limit: usize,
77) -> Result<(), FormattingError> {
78    let mut traverser = traverse_partial_payload_with_types(
79        partial_payload,
80        expected_start,
81        check_exact_end,
82        current_depth,
83        context.schema,
84        type_id,
85        depth_limit,
86    );
87    if let PrintMode::MultiLine {
88        first_line_indent, ..
89    } = &context.print_mode
90    {
91        write!(f, "{:first_line_indent$}", "")?;
92    }
93    format_value_tree(f, &mut traverser, context)?;
94    if check_exact_end {
95        consume_end_event(&mut traverser)?;
96    }
97    Ok(())
98}
99
100fn consume_end_event<E: FormattableCustomExtension>(
101    traverser: &mut TypedTraverser<E>,
102) -> Result<(), FormattingError> {
103    traverser.consume_end_event().map_err(FormattingError::Sbor)
104}
105
106fn consume_container_end<E: FormattableCustomExtension>(
107    traverser: &mut TypedTraverser<E>,
108) -> Result<(), FormattingError> {
109    traverser
110        .consume_container_end_event()
111        .map_err(FormattingError::Sbor)
112}
113
114fn format_value_tree<F: fmt::Write, E: FormattableCustomExtension>(
115    f: &mut F,
116    traverser: &mut TypedTraverser<E>,
117    context: &RustLikeDisplayContext<'_, '_, E>,
118) -> Result<(), FormattingError> {
119    let typed_event = traverser.next_event();
120    match typed_event.event {
121        ContainerStart(type_id, container_header) => {
122            let parent_depth = typed_event.location.typed_container_path.len();
123            match container_header {
124                ContainerHeader::Tuple(header) => {
125                    format_tuple(f, traverser, context, type_id, header, parent_depth)
126                }
127                ContainerHeader::EnumVariant(header) => {
128                    format_enum_variant(f, traverser, context, type_id, header, parent_depth)
129                }
130                ContainerHeader::Array(header) => {
131                    format_array(f, traverser, context, type_id, header, parent_depth)
132                }
133                ContainerHeader::Map(header) => {
134                    format_map(f, traverser, context, type_id, header, parent_depth)
135                }
136            }
137        }
138        TerminalValue(type_id, value_ref) => format_terminal_value(f, context, type_id, value_ref),
139        _ => Err(FormattingError::Sbor(
140            typed_event
141                .display_as_unexpected_event("ContainerStart | TerminalValue", context.schema),
142        )),
143    }
144}
145
146fn format_tuple<F: fmt::Write, E: FormattableCustomExtension>(
147    f: &mut F,
148    traverser: &mut TypedTraverser<E>,
149    context: &RustLikeDisplayContext<'_, '_, E>,
150    type_id: LocalTypeId,
151    tuple_header: TupleHeader,
152    parent_depth: usize,
153) -> Result<(), FormattingError> {
154    let tuple_data = context
155        .schema
156        .resolve_matching_tuple_metadata(type_id, tuple_header.length);
157
158    let field_count = tuple_header.length;
159
160    let closing_bracket = match (tuple_data.name, tuple_data.field_names, field_count) {
161        (None, None, 0) => {
162            write!(f, "Unit")?;
163            consume_container_end(traverser)?;
164            return Ok(());
165        }
166        (None, None, _) => {
167            write!(f, "Tuple(")?;
168            ')'
169        }
170        (None, Some(_), _) => {
171            write!(f, "Struct {{")?;
172            '}'
173        }
174        (Some(type_name), None, 0) => {
175            write!(f, "{}", type_name)?;
176            consume_container_end(traverser)?;
177            return Ok(());
178        }
179        (Some(type_name), None, _) => {
180            write!(f, "{}(", type_name)?;
181            ')'
182        }
183        (Some(type_name), Some(_), _) => {
184            write!(f, "{} {{", type_name)?;
185            '}'
186        }
187    };
188
189    match (field_count, context.print_mode) {
190        (_, PrintMode::SingleLine) => {
191            match tuple_data.field_names {
192                Some(field_names) => {
193                    write!(f, " ")?;
194                    for i in 0..field_count {
195                        if i > 0 {
196                            write!(f, ", ")?;
197                        }
198                        write!(f, "{}: ", field_names.get(i).unwrap())?;
199                        format_value_tree(f, traverser, context)?;
200                    }
201                    write!(f, " ")?;
202                }
203                _ => {
204                    for i in 0..field_count {
205                        if i > 0 {
206                            write!(f, ", ")?;
207                        }
208                        format_value_tree(f, traverser, context)?;
209                    }
210                }
211            }
212            f.write_char(closing_bracket)?;
213        }
214        (
215            _,
216            PrintMode::MultiLine {
217                indent_size: spaces_per_indent,
218                base_indent,
219                ..
220            },
221        ) => {
222            let child_indent_size = base_indent + spaces_per_indent * parent_depth;
223            let child_indent = " ".repeat(child_indent_size);
224            let parent_indent = &child_indent[0..child_indent_size - spaces_per_indent];
225            writeln!(f)?;
226            match tuple_data.field_names {
227                Some(field_names) => {
228                    for i in 0..field_count {
229                        write!(f, "{}{}: ", child_indent, field_names.get(i).unwrap())?;
230                        format_value_tree(f, traverser, context)?;
231                        writeln!(f, ",")?;
232                    }
233                }
234                _ => {
235                    for _ in 0..field_count {
236                        write!(f, "{}", child_indent)?;
237                        format_value_tree(f, traverser, context)?;
238                        writeln!(f, ",")?;
239                    }
240                }
241            }
242
243            write!(f, "{}{}", parent_indent, closing_bracket)?;
244        }
245    }
246
247    consume_container_end(traverser)?;
248    Ok(())
249}
250
251fn format_enum_variant<F: fmt::Write, E: FormattableCustomExtension>(
252    f: &mut F,
253    traverser: &mut TypedTraverser<E>,
254    context: &RustLikeDisplayContext<'_, '_, E>,
255    type_id: LocalTypeId,
256    variant_header: EnumVariantHeader,
257    parent_depth: usize,
258) -> Result<(), FormattingError> {
259    let enum_data = context.schema.resolve_matching_enum_metadata(
260        type_id,
261        variant_header.variant,
262        variant_header.length,
263    );
264
265    let enum_name = enum_data.enum_name.unwrap_or("Enum");
266    match (
267        context.options.include_enum_type_names,
268        enum_data.variant_name,
269    ) {
270        (true, Some(variant_name)) => {
271            write!(f, "{}::{}", enum_name, variant_name)?;
272        }
273        (false, Some(variant_name)) => {
274            write!(f, "{}", variant_name)?;
275        }
276        // If we don't have an enum variant name, to avoid confusion, we print
277        // the fact it's an enum regardless of the option.
278        (_, None) => {
279            write!(f, "{}::[{}]", enum_name, variant_header.variant)?;
280        }
281    }
282
283    let field_length = variant_header.length;
284
285    let closing_bracket = match (enum_data.field_names, field_length) {
286        (None, 0) => {
287            consume_container_end(traverser)?;
288            return Ok(());
289        }
290        (None, _) => {
291            write!(f, "(")?;
292            ')'
293        }
294        (Some(_), _) => {
295            write!(f, " {{")?;
296            '}'
297        }
298    };
299
300    match (field_length, context.print_mode) {
301        (_, PrintMode::SingleLine) => {
302            match enum_data.field_names {
303                Some(field_names) => {
304                    write!(f, " ")?;
305                    for i in 0..field_length {
306                        if i > 0 {
307                            write!(f, ", ")?;
308                        }
309                        write!(f, "{}: ", field_names.get(i).unwrap())?;
310                        format_value_tree(f, traverser, context)?;
311                    }
312                    write!(f, " ")?;
313                }
314                _ => {
315                    for i in 0..field_length {
316                        if i > 0 {
317                            write!(f, ", ")?;
318                        }
319                        format_value_tree(f, traverser, context)?;
320                    }
321                }
322            }
323            f.write_char(closing_bracket)?;
324        }
325        (
326            _,
327            PrintMode::MultiLine {
328                indent_size: spaces_per_indent,
329                base_indent,
330                ..
331            },
332        ) => {
333            let child_indent_size = base_indent + spaces_per_indent * parent_depth;
334            let child_indent = " ".repeat(child_indent_size);
335            let parent_indent = &child_indent[0..child_indent_size - spaces_per_indent];
336            writeln!(f)?;
337            match enum_data.field_names {
338                Some(field_names) => {
339                    for i in 0..field_length {
340                        write!(f, "{}{}: ", child_indent, field_names.get(i).unwrap())?;
341                        format_value_tree(f, traverser, context)?;
342                        writeln!(f, ",")?;
343                    }
344                }
345                _ => {
346                    for _ in 0..field_length {
347                        write!(f, "{}", child_indent)?;
348                        format_value_tree(f, traverser, context)?;
349                        writeln!(f, ",")?;
350                    }
351                }
352            }
353
354            write!(f, "{}{}", parent_indent, closing_bracket)?;
355        }
356    }
357
358    consume_container_end(traverser)?;
359    Ok(())
360}
361
362fn format_array<F: fmt::Write, E: FormattableCustomExtension>(
363    f: &mut F,
364    traverser: &mut TypedTraverser<E>,
365    context: &RustLikeDisplayContext<'_, '_, E>,
366    type_id: LocalTypeId,
367    array_header: ArrayHeader<E::CustomValueKind>,
368    parent_depth: usize,
369) -> Result<(), FormattingError> {
370    let array_data = context.schema.resolve_matching_array_metadata(type_id);
371
372    if let Some(array_name) = array_data.array_name {
373        write!(f, "{}(", array_name)?;
374    }
375
376    let child_count = array_header.length;
377
378    match (
379        child_count,
380        context.print_mode,
381        array_header.element_value_kind,
382    ) {
383        (_, _, ValueKind::U8) => {
384            write!(f, "hex(\"")?;
385            if child_count > 0 {
386                let typed_event = traverser.next_event();
387                match typed_event.event {
388                    TerminalValueBatch(_, TerminalValueBatchRef::U8(bytes)) => {
389                        f.write_str(&hex::encode(bytes))?;
390                    }
391                    _ => Err(FormattingError::Sbor(
392                        typed_event
393                            .display_as_unexpected_event("TerminalValueBatch", context.schema),
394                    ))?,
395                };
396            }
397            write!(f, "\")")?;
398        }
399        (0, _, _) => {
400            write!(f, "[]")?;
401        }
402        (_, PrintMode::SingleLine, _) => {
403            write!(f, "[")?;
404            for i in 0..child_count {
405                if i > 0 {
406                    write!(f, ", ")?;
407                }
408                format_value_tree(f, traverser, context)?;
409            }
410            write!(f, "]")?;
411        }
412        (
413            _,
414            PrintMode::MultiLine {
415                indent_size: spaces_per_indent,
416                base_indent,
417                ..
418            },
419            _,
420        ) => {
421            write!(f, "[")?;
422            let child_indent_size = base_indent + spaces_per_indent * parent_depth;
423            let child_indent = " ".repeat(child_indent_size);
424            let parent_indent = &child_indent[0..child_indent_size - spaces_per_indent];
425            writeln!(f)?;
426            for _ in 0..child_count {
427                write!(f, "{}", child_indent)?;
428                format_value_tree(f, traverser, context)?;
429                writeln!(f, ",")?;
430            }
431
432            write!(f, "{}]", parent_indent)?;
433        }
434    }
435
436    if array_data.array_name.is_some() {
437        write!(f, ")")?;
438    }
439    consume_container_end(traverser)?;
440    Ok(())
441}
442
443fn format_map<F: fmt::Write, E: FormattableCustomExtension>(
444    f: &mut F,
445    traverser: &mut TypedTraverser<E>,
446    context: &RustLikeDisplayContext<'_, '_, E>,
447    type_id: LocalTypeId,
448    map_header: MapHeader<E::CustomValueKind>,
449    parent_depth: usize,
450) -> Result<(), FormattingError> {
451    let map_data = context.schema.resolve_matching_map_metadata(type_id);
452
453    if let Some(map_name) = map_data.map_name {
454        write!(f, "{}(", map_name)?;
455    }
456
457    match (map_header.length, context.print_mode) {
458        (0, _) => {
459            write!(f, "{{}}")?;
460        }
461        (_, PrintMode::SingleLine) => {
462            write!(f, "{{ ")?;
463            for i in 0..map_header.length {
464                if i > 0 {
465                    write!(f, ", ")?;
466                }
467                format_value_tree(f, traverser, context)?;
468                write!(f, " => ")?;
469                format_value_tree(f, traverser, context)?;
470            }
471            write!(f, " }}")?;
472        }
473        (
474            _,
475            PrintMode::MultiLine {
476                indent_size: spaces_per_indent,
477                base_indent,
478                ..
479            },
480        ) => {
481            let child_indent_size = base_indent + spaces_per_indent * parent_depth;
482            let child_indent = " ".repeat(child_indent_size);
483            let parent_indent = &child_indent[0..child_indent_size - spaces_per_indent];
484            writeln!(f, "{{")?;
485            for _ in 0..map_header.length {
486                write!(f, "{}", child_indent)?;
487                format_value_tree(f, traverser, context)?;
488                write!(f, " => ")?;
489                format_value_tree(f, traverser, context)?;
490                writeln!(f, ",")?;
491            }
492
493            write!(f, "{}}}", parent_indent)?;
494        }
495    }
496
497    if map_data.map_name.is_some() {
498        write!(f, ")")?;
499    }
500    consume_container_end(traverser)?;
501    Ok(())
502}
503
504fn format_terminal_value<F: fmt::Write, E: FormattableCustomExtension>(
505    f: &mut F,
506    context: &RustLikeDisplayContext<'_, '_, E>,
507    type_id: LocalTypeId,
508    value_ref: TerminalValueRef<E::CustomTraversal>,
509) -> Result<(), FormattingError> {
510    let type_name = context
511        .schema
512        .resolve_type_metadata(type_id)
513        .and_then(|m| m.get_name());
514
515    // If the terminal value has a name, it's normally because it's in a semantic singleton wrapper -
516    // so wrap it in a "new-type-like struct"
517    if let Some(type_name) = type_name {
518        write!(f, "{}(", type_name)?;
519    }
520
521    if context.options.include_full_value_information {
522        match value_ref {
523            TerminalValueRef::Bool(value) => write!(f, "{value}")?,
524            TerminalValueRef::I8(value) => write!(f, "{value}i8")?,
525            TerminalValueRef::I16(value) => write!(f, "{value}i16")?,
526            TerminalValueRef::I32(value) => write!(f, "{value}i32")?,
527            TerminalValueRef::I64(value) => write!(f, "{value}i64")?,
528            TerminalValueRef::I128(value) => write!(f, "{value}i128")?,
529            TerminalValueRef::U8(value) => write!(f, "{value}u8")?,
530            TerminalValueRef::U16(value) => write!(f, "{value}u16")?,
531            TerminalValueRef::U32(value) => write!(f, "{value}u32")?,
532            TerminalValueRef::U64(value) => write!(f, "{value}u64")?,
533            TerminalValueRef::U128(value) => write!(f, "{value}u128")?,
534            // Debug encode strings to use default debug rust escaping, and
535            // avoid control characters affecting the string representation.
536            // This makes the encoding tied to the Rust version; but this is
537            // OK - we don't guarantee Rustlike encoding is 100% deterministic.
538            TerminalValueRef::String(value) => write!(f, "{value:?}")?,
539            TerminalValueRef::Custom(ref value) => {
540                write!(f, "{}(", value_ref.value_kind())?;
541                E::display_string_content(f, &context.custom_context, value)?;
542                write!(f, ")")?;
543            }
544        }
545    } else {
546        match value_ref {
547            TerminalValueRef::Bool(value) => write!(f, "{value}")?,
548            TerminalValueRef::I8(value) => write!(f, "{value}")?,
549            TerminalValueRef::I16(value) => write!(f, "{value}")?,
550            TerminalValueRef::I32(value) => write!(f, "{value}")?,
551            TerminalValueRef::I64(value) => write!(f, "{value}")?,
552            TerminalValueRef::I128(value) => write!(f, "{value}")?,
553            TerminalValueRef::U8(value) => write!(f, "{value}")?,
554            TerminalValueRef::U16(value) => write!(f, "{value}")?,
555            TerminalValueRef::U32(value) => write!(f, "{value}")?,
556            TerminalValueRef::U64(value) => write!(f, "{value}")?,
557            TerminalValueRef::U128(value) => write!(f, "{value}")?,
558            // Debug encode strings to use default debug rust escaping, and
559            // avoid control characters affecting the string representation.
560            // This makes the encoding tied to the Rust version; but this is
561            // OK - we don't guarantee Rustlike encoding is 100% deterministic.
562            TerminalValueRef::String(value) => write!(f, "{value:?}")?,
563            TerminalValueRef::Custom(ref value) => {
564                E::debug_string_content(f, &context.custom_context, value)?;
565            }
566        }
567    }
568
569    if type_name.is_some() {
570        write!(f, ")")?;
571    }
572    Ok(())
573}
574
575#[cfg(test)]
576mod tests {
577    use super::*;
578    use radix_rust::*;
579
580    #[derive(Sbor, Hash, Eq, PartialEq)]
581    #[allow(clippy::enum_variant_names)]
582    enum TestEnum {
583        UnitVariant,
584        SingleFieldVariant { field: u8 },
585        DoubleStructVariant { field1: u8, field2: u8 },
586    }
587
588    #[derive(Sbor)]
589    struct MyUnitStruct;
590
591    #[derive(BasicSbor)]
592    struct MyComplexTupleStruct(
593        Vec<u16>,
594        Vec<u16>,
595        Vec<u8>,
596        Vec<u8>,
597        IndexMap<TestEnum, MyFieldStruct>,
598        BTreeMap<String, MyUnitStruct>,
599        TestEnum,
600        TestEnum,
601        TestEnum,
602        MyFieldStruct,
603        Vec<MyUnitStruct>,
604        BasicValue,
605    );
606
607    #[derive(Sbor)]
608    struct MyFieldStruct {
609        field1: u64,
610        field2: Vec<String>,
611    }
612
613    #[test]
614    fn complex_value_formatting() {
615        let (type_id, schema) =
616            generate_full_schema_from_single_type::<MyComplexTupleStruct, NoCustomSchema>();
617        let value = MyComplexTupleStruct(
618            vec![1, 2, 3],
619            vec![],
620            vec![],
621            vec![1, 2, 3],
622            indexmap! {
623                TestEnum::UnitVariant => MyFieldStruct { field1: 1, field2: vec!["hello".to_string()] },
624                TestEnum::SingleFieldVariant { field: 1 } => MyFieldStruct { field1: 2, field2: vec!["world".to_string()] },
625                TestEnum::DoubleStructVariant { field1: 1, field2: 2 } => MyFieldStruct { field1: 3, field2: vec!["!".to_string()] },
626            },
627            btreemap! {
628                "hello".to_string() => MyUnitStruct,
629                "world".to_string() => MyUnitStruct,
630            },
631            TestEnum::UnitVariant,
632            TestEnum::SingleFieldVariant { field: 1 },
633            TestEnum::DoubleStructVariant {
634                field1: 3,
635                field2: 5,
636            },
637            MyFieldStruct {
638                field1: 21,
639                field2: vec!["hello".to_string(), "world!".to_string()],
640            },
641            vec![MyUnitStruct, MyUnitStruct],
642            Value::Tuple {
643                fields: vec![
644                    Value::Enum {
645                        discriminator: 32,
646                        fields: vec![],
647                    },
648                    Value::Enum {
649                        discriminator: 21,
650                        fields: vec![Value::I32 { value: -3 }],
651                    },
652                ],
653            },
654        );
655        let payload = basic_encode(&value).unwrap();
656
657        let expected_annotated_single_line = r###"MyComplexTupleStruct([1u16, 2u16, 3u16], [], hex(""), hex("010203"), { TestEnum::UnitVariant => MyFieldStruct { field1: 1u64, field2: ["hello"] }, TestEnum::SingleFieldVariant { field: 1u8 } => MyFieldStruct { field1: 2u64, field2: ["world"] }, TestEnum::DoubleStructVariant { field1: 1u8, field2: 2u8 } => MyFieldStruct { field1: 3u64, field2: ["!"] } }, { "hello" => MyUnitStruct, "world" => MyUnitStruct }, TestEnum::UnitVariant, TestEnum::SingleFieldVariant { field: 1u8 }, TestEnum::DoubleStructVariant { field1: 3u8, field2: 5u8 }, MyFieldStruct { field1: 21u64, field2: ["hello", "world!"] }, [MyUnitStruct, MyUnitStruct], Tuple(Enum::[32], Enum::[21](-3i32)))"###;
658        let display_context = ValueDisplayParameters::Annotated {
659            display_mode: DisplayMode::RustLike(RustLikeOptions::full()),
660            print_mode: PrintMode::SingleLine,
661            schema: schema.v1(),
662            custom_context: Default::default(),
663            type_id,
664            depth_limit: 64,
665        };
666        assert_eq!(
667            &BasicRawPayload::new_from_valid_slice_with_checks(&payload)
668                .unwrap()
669                .to_string(display_context),
670            expected_annotated_single_line,
671        );
672
673        let expected_annotated_multi_line = r###"MyComplexTupleStruct(
674            [
675                1u16,
676                2u16,
677                3u16,
678            ],
679            [],
680            hex(""),
681            hex("010203"),
682            {
683                TestEnum::UnitVariant => MyFieldStruct {
684                    field1: 1u64,
685                    field2: [
686                        "hello",
687                    ],
688                },
689                TestEnum::SingleFieldVariant {
690                    field: 1u8,
691                } => MyFieldStruct {
692                    field1: 2u64,
693                    field2: [
694                        "world",
695                    ],
696                },
697                TestEnum::DoubleStructVariant {
698                    field1: 1u8,
699                    field2: 2u8,
700                } => MyFieldStruct {
701                    field1: 3u64,
702                    field2: [
703                        "!",
704                    ],
705                },
706            },
707            {
708                "hello" => MyUnitStruct,
709                "world" => MyUnitStruct,
710            },
711            TestEnum::UnitVariant,
712            TestEnum::SingleFieldVariant {
713                field: 1u8,
714            },
715            TestEnum::DoubleStructVariant {
716                field1: 3u8,
717                field2: 5u8,
718            },
719            MyFieldStruct {
720                field1: 21u64,
721                field2: [
722                    "hello",
723                    "world!",
724                ],
725            },
726            [
727                MyUnitStruct,
728                MyUnitStruct,
729            ],
730            Tuple(
731                Enum::[32],
732                Enum::[21](
733                    -3i32,
734                ),
735            ),
736        )"###;
737        let display_context = ValueDisplayParameters::Annotated {
738            display_mode: DisplayMode::RustLike(RustLikeOptions::full()),
739            print_mode: PrintMode::MultiLine {
740                indent_size: 4,
741                base_indent: 8,
742                first_line_indent: 0,
743            },
744            schema: schema.v1(),
745            custom_context: Default::default(),
746            type_id,
747            depth_limit: 64,
748        };
749        assert_eq!(
750            &BasicRawPayload::new_from_valid_slice_with_checks(&payload)
751                .unwrap()
752                .to_string(display_context),
753            expected_annotated_multi_line,
754        );
755    }
756}