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 FixedPointDisplay::FromSiblingFieldWithOverride {
579 field_index,
580 byte_offset,
581 override_match,
582 override_decimals,
583 } => {
584 if $ctx.is_peek_pass {
585 0
586 } else {
587 let field_bytes = $ctx
588 .peek_bytes
589 .get(&field_index)
590 .expect("Fixed point display attempted to get field that was not provided in context - this is a bug in the schema display implementation");
591 let matches_override = (0..32).all(|i| {
592 field_bytes.get(&i).copied() == Some(override_match[i])
593 });
594 if matches_override {
595 override_decimals
596 } else {
597 *field_bytes
598 .get(&byte_offset)
599 .expect("Fixed point display attempted to get byte that was not provided in context - this is a bug in the schema display implementation")
600 }
601 }
602 }
603 };
604 if decimals > 39 {
606 return Err(FormatError::TooManyDecimalsForInt (decimals));
607 }
608 let t = <$t>::from_le_bytes(buf.try_into().unwrap());
609 write!($f, "{}", format_fixed_point(t.to_string(), decimals))?
610 }
611 }
612 Ok(())
613 }};
614}
615
616impl<W: Write, L: LinkingScheme, M> TypeVisitor<L, M> for DisplayVisitor<'_, '_, W> {
617 type Arg = Context;
618 type ReturnType = Result<(), FormatError>;
619 fn visit_enum(
620 &mut self,
621 e: &Enum<L>,
622 schema: &impl TypeResolver<LinkingScheme = L>,
623 mut context: Context,
624 ) -> Self::ReturnType {
625 if self.input.is_empty() && !e.variants.is_empty() {
626 return Err(FormatError::MissingDiscriminant {
627 type_name: e.type_name.clone(),
628 });
629 }
630
631 if matches!(context.parent_type, ParentType::Tuple(IsVirtual::No, _))
634 || context.parent_type == ParentType::Vec
635 {
636 self.output.write_str(&e.type_name)?;
637 }
638
639 let (open, close) = self.enum_delimiters(e, &context);
640 self.output.write_str(open)?;
641
642 let discriminant = self.input.advance(1)?[0];
643 let mut variants_by_discriminant =
644 e.variants.iter().filter(|v| v.discriminant == discriminant);
645 let variant = variants_by_discriminant
646 .next()
647 .ok_or(FormatError::InvalidDiscriminant {
648 type_name: e.type_name.clone(),
649 discriminant,
650 })?;
651 assert!(variants_by_discriminant.next().is_none(), "Found two enum variants with the same discriminant - the schema is malformed, cannot proceed!");
652 if variant.template.is_none() && !e.hide_tag {
653 write!(self.output, "{}", variant.name)?;
654 }
655 let is_hide_tag = if e.hide_tag {
656 IsHideTag::Yes
657 } else {
658 IsHideTag::No
659 };
660 context.parent_type = ParentType::Enum(is_hide_tag);
661 if let Some(maybe_resolved) = &variant.value {
662 let inner = schema.resolve_or_err(maybe_resolved)?;
663 inner.visit(schema, self, context)?;
664 }
665 self.output.write_str(close)?;
666 Ok(())
667 }
668
669 fn visit_struct(
670 &mut self,
671 s: &Struct<L>,
672 schema: &impl TypeResolver<LinkingScheme = L>,
673 mut context: Context,
674 ) -> Self::ReturnType {
675 let template = s
676 .template
677 .clone()
678 .unwrap_or_else(|| self.struct_default_template(s, &context, schema));
679 let mut template = template.as_str();
680
681 context.parent_type = ParentType::Struct(IsVirtual::No);
682
683 if s.peekable && !context.is_peek_pass {
690 let mut field_offsets: Vec<usize> = Vec::new();
692 let mut bytes_needed: Vec<(usize, usize)> = Vec::new();
695 self.start_peek();
696 context.is_peek_pass = true;
697 for field in s.fields.iter() {
698 field_offsets.push(self.input.peek_cursor());
699 let inner_ty = schema.resolve_or_err(&field.value)?;
702 inner_ty.visit(schema, self, context.clone())?;
705
706 bytes_needed.extend(inner_ty.parent_byte_references());
708 }
709 field_offsets.push(self.input.peek_cursor());
711 let still_peeking = !self.end_peek();
713 context.is_peek_pass = still_peeking;
714
715 let mut peek_bytes: BTreeMap<usize, BTreeMap<usize, u8>> = BTreeMap::new();
717 for (field, offset) in bytes_needed {
718 let field_end = *field_offsets
720 .get(field + 1)
721 .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.");
722 let byte_offset = field_offsets.get(field).unwrap() + offset;
724 if byte_offset > field_end {
725 return Err(FormatError::FieldReferenceOffsetOutOfBounds(byte_offset));
726 }
727 let byte = self.input.local_peek_byte(byte_offset)?;
728
729 let field_bytes = peek_bytes.entry(field).or_default();
730 field_bytes.insert(offset, byte);
731 }
732 context.peek_bytes = peek_bytes;
733 }
734
735 for field in &s.fields {
736 let was_silent = self.output.silent;
738 if field.silent {
739 self.output.silent = true;
740 }
741
742 let inner_ty = schema.resolve_or_err(&field.value)?;
743 if !field.silent && !inner_ty.is_skip() {
744 let Some((before_next_field, rest)) = template.split_once("{}") else {
745 return Err(FormatError::InsufficientTemplateSlots);
746 };
747 self.output.write_str(before_next_field)?;
748 template = rest;
749 }
750
751 inner_ty.visit(schema, self, context.clone())?;
752 self.output.silent = was_silent;
754 }
755 if template.contains("{}") {
756 return Err(FormatError::UnusedTemplateSlots);
757 }
758 self.output.write_str(template)?;
759 Ok(())
760 }
761
762 fn visit_tuple(
763 &mut self,
764 t: &Tuple<L>,
765 schema: &impl TypeResolver<LinkingScheme = L>,
766 mut context: Context,
767 ) -> Self::ReturnType {
768 let template = t
769 .template
770 .clone()
771 .unwrap_or_else(|| self.tuple_default_template(t, &context, schema));
772 let mut template = template.as_str();
773
774 let is_virtual = if tuple_displays_as_enum_contents(&context) {
775 IsVirtual::Yes
776 } else {
777 IsVirtual::No
778 };
779 let trivial = if t.fields.len() == 1 {
780 IsTrivial::Yes
781 } else {
782 IsTrivial::No
783 };
784 context.parent_type = ParentType::Tuple(is_virtual, trivial);
785
786 if t.peekable && !context.is_peek_pass {
789 let mut field_offsets: Vec<usize> = Vec::new();
790 let mut bytes_needed: Vec<(usize, usize)> = Vec::new();
792 self.start_peek();
793 context.is_peek_pass = true;
794 for field in t.fields.iter() {
795 field_offsets.push(self.input.peek_cursor());
796 let inner_ty = schema.resolve_or_err(&field.value)?;
797 inner_ty.visit(schema, self, context.clone())?;
798
799 bytes_needed.extend(inner_ty.parent_byte_references());
800 }
801 field_offsets.push(self.input.peek_cursor());
802 let still_peeking = !self.end_peek();
803 context.is_peek_pass = still_peeking;
804
805 let mut peek_bytes: BTreeMap<usize, BTreeMap<usize, u8>> = BTreeMap::new();
806 for (field, offset) in bytes_needed {
807 let field_end = *field_offsets
808 .get(field + 1)
809 .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.");
810 let byte_offset = field_offsets.get(field).unwrap() + offset;
812 if byte_offset > field_end {
813 return Err(FormatError::FieldReferenceOffsetOutOfBounds(byte_offset));
814 }
815 let byte = self.input.local_peek_byte(byte_offset)?;
816
817 let field_bytes = peek_bytes.entry(field).or_default();
818 field_bytes.insert(offset, byte);
819 }
820 context.peek_bytes = peek_bytes;
821 }
822
823 for field in &t.fields {
824 let was_silent = self.output.silent;
826 if field.silent {
827 self.output.silent = true;
828 }
829 if !field.silent {
830 let Some((before_next_field, rest)) = template.split_once("{}") else {
831 return Err(FormatError::InsufficientTemplateSlots);
832 };
833 self.output.write_str(before_next_field)?;
834 template = rest;
835 }
836
837 schema
838 .resolve_or_err(&field.value)?
839 .visit(schema, self, context.clone())?;
840 self.output.silent = was_silent;
842 }
843 if template.contains("{}") {
844 return Err(FormatError::UnusedTemplateSlots);
845 }
846 self.output.write_str(template)?;
847 Ok(())
848 }
849
850 fn visit_option(
851 &mut self,
852 value: &L::TypeLink,
853 schema: &impl TypeResolver<LinkingScheme = L>,
854 context: Self::Arg,
855 ) -> Self::ReturnType {
856 let discriminant = self.input.advance(1)?[0];
857
858 match discriminant {
859 0 => {
860 let (open, close) = self.option_none_delimiters();
861 self.output.write_str(open)?;
862 self.output.write_str(close)?;
863 }
864 1 => {
865 let (open, close) = self.option_some_delimiters();
866 self.output.write_str(open)?;
867 schema.resolve_or_err(value)?.visit(schema, self, context)?;
868 self.output.write_str(close)?;
869 }
870 _ => {
871 return Err(FormatError::InvalidDiscriminant {
872 type_name: "Option".to_string(),
873 discriminant,
874 })
875 }
876 }
877
878 Ok(())
879 }
880
881 fn visit_primitive(
882 &mut self,
883 p: crate::schema::Primitive,
884 _schema: &impl TypeResolver<LinkingScheme = L>,
885 context: Context,
886 ) -> Self::ReturnType {
887 match p {
888 Primitive::Float32 => {
889 let value = self.input.advance(4)?;
890 let value = f32::from_le_bytes(value.try_into().unwrap());
891 write!(self.output, "{value}")?;
892 Ok(())
893 }
894 Primitive::Float64 => {
895 let value = self.input.advance(8)?;
896 let value = f64::from_le_bytes(value.try_into().unwrap());
897 write!(self.output, "{value}")?;
898 Ok(())
899 }
900 Primitive::Boolean => {
901 let value = self.input.advance(1)?;
902 match value[0] {
903 0 => self.output.write_str("false")?,
904 1 => self.output.write_str("true")?,
905 _ => {
906 return Err(FormatError::InvalidDiscriminant {
907 type_name: "bool".to_string(),
908 discriminant: value[0],
909 });
910 }
911 }
912 Ok(())
913 }
914 Primitive::Integer(int, display) => match int {
915 IntegerType::i8 => display_int!(i8, self.input, display, self.output, context),
916 IntegerType::i16 => display_int!(i16, self.input, display, self.output, context),
917 IntegerType::i32 => display_int!(i32, self.input, display, self.output, context),
918 IntegerType::i64 => display_int!(i64, self.input, display, self.output, context),
919 IntegerType::i128 => display_int!(i128, self.input, display, self.output, context),
920 IntegerType::u8 => display_int!(u8, self.input, display, self.output, context),
921 IntegerType::u16 => display_int!(u16, self.input, display, self.output, context),
922 IntegerType::u32 => display_int!(u32, self.input, display, self.output, context),
923 IntegerType::u64 => display_int!(u64, self.input, display, self.output, context),
924 IntegerType::u128 => display_int!(u128, self.input, display, self.output, context),
925 },
926 Primitive::ByteArray { len, display } => {
927 self.display_byte_sequence(len, display, context)
928 }
929 Primitive::ByteVec { display } => {
930 let len = self
931 .read_usize_borsh()
932 .or(Err(FormatError::MissingVecLength))?;
933 self.display_byte_sequence(len, display, context)
934 }
935 Primitive::String => {
936 let len = self
937 .read_usize_borsh()
938 .or(Err(FormatError::MissingStringLength))?;
939 let content = self.input.advance(len)?;
940 let content = std::str::from_utf8(content)?;
941 write!(self.output, "\"{content}\"")?;
942 Ok(())
943 }
944 Primitive::Skip { len } => {
945 self.input.advance(len)?;
946 Ok(())
947 }
948 }
949 }
950
951 fn visit_array(
952 &mut self,
953 len: &usize,
954 value: &L::TypeLink,
955 schema: &impl TypeResolver<LinkingScheme = L>,
956 mut context: Context,
957 ) -> Self::ReturnType {
958 let inner = schema.resolve_or_err(value)?;
959 let (open, close) = self.vec_delimiters(&context);
960 self.output.write_str(open)?;
961 context.parent_type = ParentType::Vec;
962 for i in 0..*len {
963 if i > 0 {
964 self.output.write_str(", ")?;
965 }
966 inner.visit(schema, self, context.clone())?;
967 }
968 self.output.write_str(close)?;
969 Ok(())
970 }
971
972 fn visit_vec(
973 &mut self,
974 value: &L::TypeLink,
975 schema: &impl TypeResolver<LinkingScheme = L>,
976 mut context: Context,
977 ) -> Self::ReturnType {
978 let len = self.read_usize_borsh()?;
979 let inner = schema.resolve_or_err(value)?;
980 let (open, close) = self.vec_delimiters(&context);
981 self.output.write_str(open)?;
982 context.parent_type = ParentType::Vec;
983 for i in 0..len {
984 if i > 0 {
985 self.output.write_str(", ")?;
986 }
987 inner.visit(schema, self, context.clone())?;
988 }
989 self.output.write_str(close)?;
990 Ok(())
991 }
992
993 fn visit_map(
994 &mut self,
995 key: &L::TypeLink,
996 value: &L::TypeLink,
997 schema: &impl TypeResolver<LinkingScheme = L>,
998 mut context: Context,
999 ) -> Self::ReturnType {
1000 let len = self.read_usize_borsh()?;
1001 let key = schema.resolve_or_err(key)?;
1002 let value = schema.resolve_or_err(value)?;
1003 let (open, close) = self.map_delimiters(&context);
1004 self.output.write_str(open)?;
1005 context.parent_type = ParentType::Map;
1006 for i in 0..len {
1007 if i > 0 {
1008 self.output.write_str(", ")?;
1009 }
1010 key.visit(schema, self, context.clone())?;
1011 self.output.write_str(": ")?;
1012 value.visit(schema, self, context.clone())?;
1013 }
1014 self.output.write_str(close)?;
1015 Ok(())
1016 }
1017}