1use std::collections::BTreeMap;
2use std::fmt::Write;
3
4use thiserror::Error;
5
6use crate::schema::Primitive;
7use crate::ty::visitor::{ResolutionError, TypeResolver, TypeVisitor};
8use crate::ty::{
9 byte_display, ByteDisplay, Enum, FixedPointDisplay, IntegerDisplay, IntegerType, LinkingScheme,
10 Struct, Tuple,
11};
12
13type Delimiters = (&'static str, &'static str);
14
15pub type Result<T, E = FormatError> = core::result::Result<T, E>;
16
17pub const MAX_INPUT_CHUNK: usize = 65;
19
20#[derive(Debug, Error, Clone)]
21pub enum FormatError {
22 #[error("Core error: {0}")]
23 Core(#[from] core::fmt::Error),
24 #[error("The byte sequence could not be formatted for display: {0}")]
25 InvalidBytes(#[from] byte_display::ByteFormatError),
26 #[error("The input is not a valid utf-8 string: {0}")]
27 InvalidString(#[from] core::str::Utf8Error),
28 #[error("Invalid discriminant `{discriminant}` for {type_name}")]
29 InvalidDiscriminant { type_name: String, discriminant: u8 },
30 #[error(transparent)]
31 UnresolvedType(#[from] ResolutionError),
32 #[error("A discriminant is required for items of type `{type_name}` but the input ended without providing one.")]
33 MissingDiscriminant { type_name: String },
34 #[error("The input claimed to provide an integer {claimed_size} bytes wide, but the maximum allowed size is 16 bytes.")]
35 IntegerTooLarge { claimed_size: u8 },
36 #[error("The input claimed to provide an integer {claimed_size} bytes wide, but only provided {bytes_available} additional bytes of input.")]
37 MissingIntegerInput {
38 claimed_size: u8,
39 bytes_available: u8,
40 },
41 #[error("The input claimed to provide a byte array {claimed_size} bytes wide, but only provided {bytes_available} additional bytes of input.")]
42 MissingBytesInput {
43 claimed_size: usize,
44 bytes_available: usize,
45 },
46 #[error("The input's attributes reference a sibling field with a byte offset, but offset was outside the range of the field's value.")]
47 FieldReferenceOffsetOutOfBounds(usize),
48 #[error("Fixed-point integer formatting specified more decimals than is plausible for an integer: {0}")]
49 TooManyDecimalsForInt(u8),
50 #[error("The input should have contained a vector but did not provide one.")]
51 MissingVecLength,
52 #[error("The input should have contained a string but did not provide one.")]
53 MissingStringLength,
54 #[error("The provided input had leftover bytes that weren't displayed.")]
55 UnusedInput,
56 #[error(
57 "A structs's display template did not provide sufficient slots to display its fields."
58 )]
59 InsufficientTemplateSlots,
60 #[error("A struct's display template had more slots than the struct had fields.")]
61 UnusedTemplateSlots,
62}
63
64pub struct Output<'a, W> {
65 f: &'a mut W,
66 silent: bool,
67 peeking: u32,
70}
71
72impl<'a, W> Output<'a, W> {
73 pub fn new(f: &'a mut W) -> Self {
74 Self {
75 f,
76 silent: false,
77 peeking: 0,
78 }
79 }
80
81 pub fn start_peek(&mut self) {
82 self.peeking = self.peeking.checked_add(1).unwrap();
83 }
84
85 pub fn end_peek(&mut self) {
86 self.peeking = self
87 .peeking
88 .checked_sub(1)
89 .expect("Underflow when ending peek. This is a bug in the schema display logic.")
90 }
91
92 pub fn peeking(&self) -> bool {
93 self.peeking > 0
94 }
95}
96
97impl<W: core::fmt::Write> core::fmt::Write for Output<'_, W> {
98 fn write_str(&mut self, s: &str) -> core::fmt::Result {
99 if self.peeking() {
100 return Ok(());
101 }
102 if self.silent {
103 return Ok(());
104 }
105 self.f.write_str(s)
106 }
107}
108
109pub struct Input<'a> {
110 buf: &'a mut &'a [u8],
111 peeking: bool,
112 peek_origins_stack: Vec<usize>,
115 peek_cursor: usize,
118}
119
120impl<'a> Input<'a> {
121 pub fn new(buf: &'a mut &'a [u8]) -> Self {
122 Self {
123 buf,
124 peeking: false,
125 peek_origins_stack: Vec::new(),
126 peek_cursor: 0,
127 }
128 }
129
130 pub fn is_empty(&self) -> bool {
131 self.buf.is_empty()
132 }
133
134 pub fn len(&self) -> usize {
135 self.buf.len()
136 }
137
138 pub(crate) fn check_remaining_bytes(&self, mut len: usize) -> Result<(), FormatError> {
139 if self.peeking {
140 len += self.peek_cursor;
141 }
142 if self.buf.len() < len {
143 return Err(FormatError::MissingBytesInput {
144 claimed_size: len,
145 bytes_available: self.buf.len(),
146 });
147 }
148 Ok(())
149 }
150
151 pub fn advance(&mut self, len: usize) -> Result<&[u8], FormatError> {
154 self.check_remaining_bytes(len)?;
155 if self.peeking {
156 let end_cursor = self.peek_cursor + len;
157 let slice = &self.buf[self.peek_cursor..end_cursor];
158 self.peek_cursor = end_cursor;
159 Ok(slice)
160 } else {
161 let (leading, rest) = self.buf.split_at(len);
162 *self.buf = rest;
163 Ok(leading)
164 }
165 }
166
167 pub fn local_peek_bytes(&self, len: usize) -> Result<&[u8], FormatError> {
172 self.check_remaining_bytes(len)?;
173 let end = self.peek_cursor + len;
175 Ok(&self.buf[self.peek_cursor..end])
176 }
177
178 pub fn local_peek_byte(&self, offset: usize) -> Result<u8, FormatError> {
183 self.check_remaining_bytes(offset)?;
184 let offset = self.peek_cursor + offset;
186 Ok(self.buf[offset])
187 }
188
189 pub fn start_peek(&mut self) {
190 self.peeking = true;
191 self.peek_origins_stack.push(self.peek_cursor);
192 }
193
194 pub fn peek_cursor(&self) -> usize {
196 self.peek_cursor.checked_sub(*self.peek_origins_stack.last().expect("peek_cursor() was called while no peek is ongoing. This is a bug in the schema display logic.")).expect("The peek cursor offset calculation underflowed. This is a bug in the schema display logic.")
197 }
198
199 pub fn end_peek(&mut self) -> bool {
206 self.peek_origins_stack.pop().expect("end_peek() was called but the peek stack was empty. This is a bug in the schema display logic.");
207 if self.peek_origins_stack.is_empty() {
209 self.peeking = false;
210 self.peek_cursor = 0;
211 true
212 } else {
213 false
214 }
215 }
216}
217
218fn tuple_displays_as_enum_contents(context: &Context) -> bool {
221 context.parent_type == ParentType::Enum(IsHideTag::No)
222 || context.parent_type == ParentType::Tuple(IsVirtual::Yes, IsTrivial::Yes)
223}
224
225pub struct DisplayVisitor<'a, 'fmt, W> {
226 input: Input<'a>,
227 output: Output<'fmt, W>,
228}
229
230impl<'a, 'fmt, W> DisplayVisitor<'a, 'fmt, W> {
241 pub fn new(input: &'a mut &'a [u8], f: &'fmt mut W) -> Self {
242 Self {
243 input: Input::new(input),
244 output: Output::new(f),
245 }
246 }
247
248 pub fn has_displayed_whole_input(&self) -> bool {
249 self.input.is_empty()
250 }
251
252 fn start_peek(&mut self) {
253 self.output.start_peek();
254 self.input.start_peek();
255 }
256
257 fn end_peek(&mut self) -> bool {
258 self.output.end_peek();
259 self.input.end_peek()
260 }
261
262 fn tuple_delimiters<L: LinkingScheme>(
263 &self,
264 tuple: &Tuple<L>,
265 context: &Context,
266 schema: &impl TypeResolver<LinkingScheme = L>,
267 ) -> Delimiters {
268 let first_child_is_primitive = schema
269 .resolve_or_err(&tuple.fields[0].value)
270 .is_ok_and(|v| v.is_primitive());
271
272 if tuple.fields.len() == 1
273 && !(tuple_displays_as_enum_contents(context) && first_child_is_primitive)
274 {
275 ("", "")
276 } else {
277 ("(", ")")
278 }
279 }
280
281 fn enum_delimiters<L: LinkingScheme>(&mut self, _e: &Enum<L>, context: &Context) -> Delimiters {
282 match context.parent_type {
283 ParentType::Tuple(_, IsTrivial::Yes)
284 | ParentType::Enum(_)
285 | ParentType::Vec
286 | ParentType::Map => (".", ""),
287 ParentType::Tuple(_, _) | ParentType::Struct(_) | ParentType::None => ("", ""),
288 }
289 }
290
291 fn struct_delimiters<L: LinkingScheme>(&self, s: &Struct<L>, context: &Context) -> Delimiters {
292 if s.fields.is_empty() {
293 return ("", "");
294 }
295 match context.parent_type {
296 ParentType::Tuple(IsVirtual::Yes, _) => (" { ", " }"),
297 ParentType::Struct(_)
298 | ParentType::None
299 | ParentType::Tuple(_, _)
300 | ParentType::Vec
301 | ParentType::Map => ("{ ", " }"),
302 ParentType::Enum(_) => (" { ", " }"),
303 }
304 }
305
306 fn struct_default_template<L: LinkingScheme>(
316 &self,
317 s: &Struct<L>,
318 context: &Context,
319 schema: &impl TypeResolver<LinkingScheme = L>,
320 ) -> String {
321 let mut template = String::new();
322 let (opener, closer) = self.struct_delimiters(s, context);
323 template.push_str(opener);
324 if s.fields.is_empty() {
325 template.push_str(&s.type_name);
326 } else {
327 for (i, field) in s.fields.iter().enumerate() {
328 if field.silent {
329 continue;
330 }
331 if schema
332 .resolve_or_err(&field.value)
333 .is_ok_and(|inner| inner.is_skip())
334 {
335 continue;
336 }
337 if i > 0 {
338 template.push_str(self.item_separator());
339 }
340 template.push_str(&field.display_name);
341 template.push_str(": {}");
342 }
343 }
344 template.push_str(closer);
345 template
346 }
347
348 fn tuple_default_template<L: LinkingScheme>(
358 &self,
359 t: &Tuple<L>,
360 context: &Context,
361 schema: &impl TypeResolver<LinkingScheme = L>,
362 ) -> String {
363 let mut template = String::new();
364 let (opener, closer) = self.tuple_delimiters(t, context, schema);
365 template.push_str(opener);
366 for (i, field) in t.fields.iter().enumerate() {
367 if field.silent {
368 continue;
369 }
370 if i > 0 {
371 template.push_str(self.item_separator());
372 }
373 template.push_str("{}");
374 }
375 template.push_str(closer);
376 template
377 }
378
379 fn option_none_delimiters(&self) -> Delimiters {
380 ("None", "")
381 }
382
383 fn option_some_delimiters(&self) -> Delimiters {
384 ("", "")
385 }
386
387 fn map_delimiters(&mut self, context: &Context) -> Delimiters {
388 match context.parent_type {
389 ParentType::Tuple(IsVirtual::Yes, _) => (" { ", " }"),
390 ParentType::Struct(_)
391 | ParentType::None
392 | ParentType::Tuple(_, _)
393 | ParentType::Vec
394 | ParentType::Enum(_)
395 | ParentType::Map => ("{ ", " }"),
396 }
397 }
398
399 fn vec_delimiters(&mut self, context: &Context) -> Delimiters {
400 match context.parent_type {
401 ParentType::Tuple(IsVirtual::Yes, _) => (" [", "]"),
402 ParentType::None
403 | ParentType::Struct(_)
404 | ParentType::Tuple(_, _)
405 | ParentType::Vec
406 | ParentType::Enum(_)
407 | ParentType::Map => ("[", "]"),
408 }
409 }
410
411 fn item_separator(&self) -> &'static str {
412 ", "
413 }
414}
415
416impl<W: Write> DisplayVisitor<'_, '_, W> {
417 pub fn read_usize_borsh(&mut self) -> Result<usize, FormatError> {
418 if self.input.len() < 4 {
419 return Err(FormatError::MissingIntegerInput {
420 claimed_size: 4,
421 bytes_available: self.input.len() as u8,
422 });
423 }
424 let len = u32::from_le_bytes(
425 self.input
426 .advance(4)?
427 .try_into()
428 .expect("Converting [u8;4] to u32 is infallible"),
429 ) as usize;
430 Ok(len)
431 }
432
433 pub fn display_byte_sequence(
434 &mut self,
435 len: usize,
436 display: ByteDisplay,
437 _context: Context,
438 ) -> Result<(), FormatError> {
439 self.input.check_remaining_bytes(len)?;
440
441 if len > MAX_INPUT_CHUNK {
442 display.format(self.input.advance(MAX_INPUT_CHUNK)?, &mut self.output)?;
443 self.output.write_fmt(format_args!(
444 " (trailing {} bytes truncated)",
445 len - MAX_INPUT_CHUNK
446 ))?;
447 self.input.advance(len - MAX_INPUT_CHUNK)?;
448 } else {
449 display.format(self.input.advance(len)?, &mut self.output)?;
450 }
451 Ok(())
452 }
453}
454
455#[derive(Clone, Debug)]
456pub struct Context {
457 parent_type: ParentType,
458 is_peek_pass: bool,
461 peek_bytes: BTreeMap<usize, BTreeMap<usize, u8>>,
465}
466
467#[derive(Debug, Clone, Copy, PartialEq, Eq)]
468pub enum IsVirtual {
469 Yes,
470 No,
471}
472
473#[derive(Debug, Clone, Copy, PartialEq, Eq)]
475pub enum IsTrivial {
476 Yes,
477 No,
478}
479
480#[derive(Debug, Clone, Copy, PartialEq, Eq)]
482pub enum IsHideTag {
483 Yes,
484 No,
485}
486
487#[derive(Debug, Clone, Copy, PartialEq, Eq)]
488pub enum ParentType {
489 None,
490 Struct(IsVirtual),
491 Tuple(IsVirtual, IsTrivial),
492 Enum(IsHideTag),
493 Vec,
494 Map,
495}
496
497impl Default for Context {
498 fn default() -> Self {
499 Self {
500 parent_type: ParentType::None,
501 is_peek_pass: false,
502 peek_bytes: BTreeMap::new(),
503 }
504 }
505}
506
507fn format_fixed_point(number_str: String, decimals: u8) -> String {
519 let (number_str, is_negative) = match number_str.strip_prefix('-') {
521 Some(str) => (str.to_string(), true),
522 None => (number_str, false),
523 };
524 let mut number_str = format!("{:0>pad$}", number_str, pad = (decimals + 1) as usize);
526 let decimal_idx = number_str.len().checked_sub(decimals.into()).expect("We just formatted the string to be wider than the number of decimals - should never underflow");
528 number_str.insert(decimal_idx, '.');
529 let mut number_str = number_str
531 .trim_end_matches('0')
532 .trim_end_matches('.')
533 .to_string();
534 if is_negative {
536 number_str.insert(0, '-');
537 }
538 number_str
539}
540
541macro_rules! display_int {
544 ($t:ident, $input:expr, $disp:expr, $f:expr, $ctx:expr) => {{
545 let size = IntegerType::$t.size();
546 if $input.len() < size {
547 return Err(FormatError::MissingIntegerInput {
548 claimed_size: size as u8,
549 bytes_available: $input.len() as u8,
550 });
551 }
552 let buf = $input.advance(size)?;
553 match $disp {
554 IntegerDisplay::Hex => {
555 write!($f, "{:#x}", <$t>::from_le_bytes(buf.try_into().unwrap()))?
556 }
557 IntegerDisplay::Decimal => {
558 write!($f, "{}", <$t>::from_le_bytes(buf.try_into().unwrap()))?
559 }
560 IntegerDisplay::FixedPoint(decimal_spec) => {
561 let decimals = match decimal_spec {
562 FixedPointDisplay::Decimals(d) => d,
563 FixedPointDisplay::FromSiblingField {
564 field_index,
565 byte_offset,
566 } => {
567 if $ctx.is_peek_pass {
568 0
569 } else {
570 *$ctx
571 .peek_bytes
572 .get(&field_index)
573 .expect("Fixed point display attempted to get field that was not provided in context - this is a bug in the schema display implementation")
574 .get(&byte_offset)
575 .expect("Fixed point display attempted to get byte that was not provided in context - this is a bug in the schema display implementation")
576 }
577 }
578 };
579 if decimals > 39 {
581 return Err(FormatError::TooManyDecimalsForInt (decimals));
582 }
583 let t = <$t>::from_le_bytes(buf.try_into().unwrap());
584 write!($f, "{}", format_fixed_point(t.to_string(), decimals))?
585 }
586 }
587 Ok(())
588 }};
589}
590
591impl<W: Write, L: LinkingScheme, M> TypeVisitor<L, M> for DisplayVisitor<'_, '_, W> {
592 type Arg = Context;
593 type ReturnType = Result<(), FormatError>;
594 fn visit_enum(
595 &mut self,
596 e: &Enum<L>,
597 schema: &impl TypeResolver<LinkingScheme = L>,
598 mut context: Context,
599 ) -> Self::ReturnType {
600 if self.input.is_empty() && !e.variants.is_empty() {
601 return Err(FormatError::MissingDiscriminant {
602 type_name: e.type_name.clone(),
603 });
604 }
605
606 if matches!(context.parent_type, ParentType::Tuple(IsVirtual::No, _))
609 || context.parent_type == ParentType::Vec
610 {
611 self.output.write_str(&e.type_name)?;
612 }
613
614 let (open, close) = self.enum_delimiters(e, &context);
615 self.output.write_str(open)?;
616
617 let discriminant = self.input.advance(1)?[0];
618 let mut variants_by_discriminant =
619 e.variants.iter().filter(|v| v.discriminant == discriminant);
620 let variant = variants_by_discriminant
621 .next()
622 .ok_or(FormatError::InvalidDiscriminant {
623 type_name: e.type_name.clone(),
624 discriminant,
625 })?;
626 assert!(variants_by_discriminant.next().is_none(), "Found two enum variants with the same discriminant - the schema is malformed, cannot proceed!");
627 if variant.template.is_none() && !e.hide_tag {
628 write!(self.output, "{}", variant.name)?;
629 }
630 let is_hide_tag = if e.hide_tag {
631 IsHideTag::Yes
632 } else {
633 IsHideTag::No
634 };
635 context.parent_type = ParentType::Enum(is_hide_tag);
636 if let Some(maybe_resolved) = &variant.value {
637 let inner = schema.resolve_or_err(maybe_resolved)?;
638 inner.visit(schema, self, context)?;
639 }
640 self.output.write_str(close)?;
641 Ok(())
642 }
643
644 fn visit_struct(
645 &mut self,
646 s: &Struct<L>,
647 schema: &impl TypeResolver<LinkingScheme = L>,
648 mut context: Context,
649 ) -> Self::ReturnType {
650 let template = s
651 .template
652 .clone()
653 .unwrap_or_else(|| self.struct_default_template(s, &context, schema));
654 let mut template = template.as_str();
655
656 context.parent_type = ParentType::Struct(IsVirtual::No);
657
658 if s.peekable && !context.is_peek_pass {
665 let mut field_offsets: Vec<usize> = Vec::new();
667 let mut bytes_needed: Vec<(usize, usize)> = Vec::new();
670 self.start_peek();
671 context.is_peek_pass = true;
672 for field in s.fields.iter() {
673 field_offsets.push(self.input.peek_cursor());
674 let inner_ty = schema.resolve_or_err(&field.value)?;
677 inner_ty.visit(schema, self, context.clone())?;
680
681 bytes_needed.extend(inner_ty.parent_byte_references());
683 }
684 field_offsets.push(self.input.peek_cursor());
686 let still_peeking = !self.end_peek();
688 context.is_peek_pass = still_peeking;
689
690 let mut peek_bytes: BTreeMap<usize, BTreeMap<usize, u8>> = BTreeMap::new();
692 for (field, offset) in bytes_needed {
693 let field_end = *field_offsets
695 .get(field + 1)
696 .expect("A struct's child field attribute referenced a sibling field by index that is out of bounds of the parent struct. This should not be possible in a well-constructed schema.");
697 let byte_offset = field_offsets.get(field).unwrap() + offset;
699 if byte_offset > field_end {
700 return Err(FormatError::FieldReferenceOffsetOutOfBounds(byte_offset));
701 }
702 let byte = self.input.local_peek_byte(byte_offset)?;
703
704 let field_bytes = peek_bytes.entry(field).or_default();
705 field_bytes.insert(offset, byte);
706 }
707 context.peek_bytes = peek_bytes;
708 }
709
710 for field in &s.fields {
711 let was_silent = self.output.silent;
713 if field.silent {
714 self.output.silent = true;
715 }
716
717 let inner_ty = schema.resolve_or_err(&field.value)?;
718 if !field.silent && !inner_ty.is_skip() {
719 let Some((before_next_field, rest)) = template.split_once("{}") else {
720 return Err(FormatError::InsufficientTemplateSlots);
721 };
722 self.output.write_str(before_next_field)?;
723 template = rest;
724 }
725
726 inner_ty.visit(schema, self, context.clone())?;
727 self.output.silent = was_silent;
729 }
730 if template.contains("{}") {
731 return Err(FormatError::UnusedTemplateSlots);
732 }
733 self.output.write_str(template)?;
734 Ok(())
735 }
736
737 fn visit_tuple(
738 &mut self,
739 t: &Tuple<L>,
740 schema: &impl TypeResolver<LinkingScheme = L>,
741 mut context: Context,
742 ) -> Self::ReturnType {
743 let template = t
744 .template
745 .clone()
746 .unwrap_or_else(|| self.tuple_default_template(t, &context, schema));
747 let mut template = template.as_str();
748
749 let is_virtual = if tuple_displays_as_enum_contents(&context) {
750 IsVirtual::Yes
751 } else {
752 IsVirtual::No
753 };
754 let trivial = if t.fields.len() == 1 {
755 IsTrivial::Yes
756 } else {
757 IsTrivial::No
758 };
759 context.parent_type = ParentType::Tuple(is_virtual, trivial);
760
761 if t.peekable && !context.is_peek_pass {
764 let mut field_offsets: Vec<usize> = Vec::new();
765 let mut bytes_needed: Vec<(usize, usize)> = Vec::new();
767 self.start_peek();
768 context.is_peek_pass = true;
769 for field in t.fields.iter() {
770 field_offsets.push(self.input.peek_cursor());
771 let inner_ty = schema.resolve_or_err(&field.value)?;
772 inner_ty.visit(schema, self, context.clone())?;
773
774 bytes_needed.extend(inner_ty.parent_byte_references());
775 }
776 field_offsets.push(self.input.peek_cursor());
777 let still_peeking = !self.end_peek();
778 context.is_peek_pass = still_peeking;
779
780 let mut peek_bytes: BTreeMap<usize, BTreeMap<usize, u8>> = BTreeMap::new();
781 for (field, offset) in bytes_needed {
782 let field_end = *field_offsets
783 .get(field + 1)
784 .expect("A tuple's child field attribute referenced a sibling field by index that is out of bounds of the parent struct. This should not be possible in a well-constructed schema.");
785 let byte_offset = field_offsets.get(field).unwrap() + offset;
787 if byte_offset > field_end {
788 return Err(FormatError::FieldReferenceOffsetOutOfBounds(byte_offset));
789 }
790 let byte = self.input.local_peek_byte(byte_offset)?;
791
792 let field_bytes = peek_bytes.entry(field).or_default();
793 field_bytes.insert(offset, byte);
794 }
795 context.peek_bytes = peek_bytes;
796 }
797
798 for field in &t.fields {
799 let was_silent = self.output.silent;
801 if field.silent {
802 self.output.silent = true;
803 }
804 if !field.silent {
805 let Some((before_next_field, rest)) = template.split_once("{}") else {
806 return Err(FormatError::InsufficientTemplateSlots);
807 };
808 self.output.write_str(before_next_field)?;
809 template = rest;
810 }
811
812 schema
813 .resolve_or_err(&field.value)?
814 .visit(schema, self, context.clone())?;
815 self.output.silent = was_silent;
817 }
818 if template.contains("{}") {
819 return Err(FormatError::UnusedTemplateSlots);
820 }
821 self.output.write_str(template)?;
822 Ok(())
823 }
824
825 fn visit_option(
826 &mut self,
827 value: &L::TypeLink,
828 schema: &impl TypeResolver<LinkingScheme = L>,
829 context: Self::Arg,
830 ) -> Self::ReturnType {
831 let discriminant = self.input.advance(1)?[0];
832
833 match discriminant {
834 0 => {
835 let (open, close) = self.option_none_delimiters();
836 self.output.write_str(open)?;
837 self.output.write_str(close)?;
838 }
839 1 => {
840 let (open, close) = self.option_some_delimiters();
841 self.output.write_str(open)?;
842 schema.resolve_or_err(value)?.visit(schema, self, context)?;
843 self.output.write_str(close)?;
844 }
845 _ => {
846 return Err(FormatError::InvalidDiscriminant {
847 type_name: "Option".to_string(),
848 discriminant,
849 })
850 }
851 }
852
853 Ok(())
854 }
855
856 fn visit_primitive(
857 &mut self,
858 p: crate::schema::Primitive,
859 _schema: &impl TypeResolver<LinkingScheme = L>,
860 context: Context,
861 ) -> Self::ReturnType {
862 match p {
863 Primitive::Float32 => {
864 let value = self.input.advance(4)?;
865 let value = f32::from_le_bytes(value.try_into().unwrap());
866 write!(self.output, "{value}")?;
867 Ok(())
868 }
869 Primitive::Float64 => {
870 let value = self.input.advance(8)?;
871 let value = f64::from_le_bytes(value.try_into().unwrap());
872 write!(self.output, "{value}")?;
873 Ok(())
874 }
875 Primitive::Boolean => {
876 let value = self.input.advance(1)?;
877 match value[0] {
878 0 => self.output.write_str("false")?,
879 1 => self.output.write_str("true")?,
880 _ => {
881 return Err(FormatError::InvalidDiscriminant {
882 type_name: "bool".to_string(),
883 discriminant: value[0],
884 });
885 }
886 }
887 Ok(())
888 }
889 Primitive::Integer(int, display) => match int {
890 IntegerType::i8 => display_int!(i8, self.input, display, self.output, context),
891 IntegerType::i16 => display_int!(i16, self.input, display, self.output, context),
892 IntegerType::i32 => display_int!(i32, self.input, display, self.output, context),
893 IntegerType::i64 => display_int!(i64, self.input, display, self.output, context),
894 IntegerType::i128 => display_int!(i128, self.input, display, self.output, context),
895 IntegerType::u8 => display_int!(u8, self.input, display, self.output, context),
896 IntegerType::u16 => display_int!(u16, self.input, display, self.output, context),
897 IntegerType::u32 => display_int!(u32, self.input, display, self.output, context),
898 IntegerType::u64 => display_int!(u64, self.input, display, self.output, context),
899 IntegerType::u128 => display_int!(u128, self.input, display, self.output, context),
900 },
901 Primitive::ByteArray { len, display } => {
902 self.display_byte_sequence(len, display, context)
903 }
904 Primitive::ByteVec { display } => {
905 let len = self
906 .read_usize_borsh()
907 .or(Err(FormatError::MissingVecLength))?;
908 self.display_byte_sequence(len, display, context)
909 }
910 Primitive::String => {
911 let len = self
912 .read_usize_borsh()
913 .or(Err(FormatError::MissingStringLength))?;
914 let content = self.input.advance(len)?;
915 let content = std::str::from_utf8(content)?;
916 write!(self.output, "\"{content}\"")?;
917 Ok(())
918 }
919 Primitive::Skip { len } => {
920 self.input.advance(len)?;
921 Ok(())
922 }
923 }
924 }
925
926 fn visit_array(
927 &mut self,
928 len: &usize,
929 value: &L::TypeLink,
930 schema: &impl TypeResolver<LinkingScheme = L>,
931 mut context: Context,
932 ) -> Self::ReturnType {
933 let inner = schema.resolve_or_err(value)?;
934 let (open, close) = self.vec_delimiters(&context);
935 self.output.write_str(open)?;
936 context.parent_type = ParentType::Vec;
937 for i in 0..*len {
938 if i > 0 {
939 self.output.write_str(", ")?;
940 }
941 inner.visit(schema, self, context.clone())?;
942 }
943 self.output.write_str(close)?;
944 Ok(())
945 }
946
947 fn visit_vec(
948 &mut self,
949 value: &L::TypeLink,
950 schema: &impl TypeResolver<LinkingScheme = L>,
951 mut context: Context,
952 ) -> Self::ReturnType {
953 let len = self.read_usize_borsh()?;
954 let inner = schema.resolve_or_err(value)?;
955 let (open, close) = self.vec_delimiters(&context);
956 self.output.write_str(open)?;
957 context.parent_type = ParentType::Vec;
958 for i in 0..len {
959 if i > 0 {
960 self.output.write_str(", ")?;
961 }
962 inner.visit(schema, self, context.clone())?;
963 }
964 self.output.write_str(close)?;
965 Ok(())
966 }
967
968 fn visit_map(
969 &mut self,
970 key: &L::TypeLink,
971 value: &L::TypeLink,
972 schema: &impl TypeResolver<LinkingScheme = L>,
973 mut context: Context,
974 ) -> Self::ReturnType {
975 let len = self.read_usize_borsh()?;
976 let key = schema.resolve_or_err(key)?;
977 let value = schema.resolve_or_err(value)?;
978 let (open, close) = self.map_delimiters(&context);
979 self.output.write_str(open)?;
980 context.parent_type = ParentType::Map;
981 for i in 0..len {
982 if i > 0 {
983 self.output.write_str(", ")?;
984 }
985 key.visit(schema, self, context.clone())?;
986 self.output.write_str(": ")?;
987 value.visit(schema, self, context.clone())?;
988 }
989 self.output.write_str(close)?;
990 Ok(())
991 }
992}