1use super::{
18 decode::Decoder,
19 encode::Encoder,
20 env_types::{
21 self,
22 CustomTypeDecoder,
23 CustomTypeEncoder,
24 EnvTypesTranscoder,
25 PathKey,
26 TypesByPath,
27 },
28 scon::Value,
29 AccountId32,
30};
31
32use anyhow::Result;
33use scale::Output;
34use scale_info::{
35 PortableRegistry,
36 TypeInfo,
37};
38use std::{
39 collections::HashMap,
40 fmt::Debug,
41};
42
43pub struct Transcoder {
46 env_types: EnvTypesTranscoder,
47}
48
49impl Transcoder {
50 pub fn new(env_types: EnvTypesTranscoder) -> Self {
51 Self { env_types }
52 }
53
54 pub fn encode<O>(
55 &self,
56 registry: &PortableRegistry,
57 type_id: u32,
58 value: &Value,
59 output: &mut O,
60 ) -> Result<()>
61 where
62 O: Output + Debug,
63 {
64 let encoder = Encoder::new(registry, &self.env_types);
65 encoder.encode(type_id, value, output)
66 }
67
68 pub fn decode(
69 &self,
70 registry: &PortableRegistry,
71 type_id: u32,
72 input: &mut &[u8],
73 ) -> Result<Value> {
74 let decoder = Decoder::new(registry, &self.env_types);
75 decoder.decode(type_id, input)
76 }
77}
78
79pub struct TranscoderBuilder {
81 types_by_path: TypesByPath,
82 encoders: HashMap<u32, Box<dyn CustomTypeEncoder>>,
83 decoders: HashMap<u32, Box<dyn CustomTypeDecoder>>,
84}
85
86impl TranscoderBuilder {
87 pub fn new(registry: &PortableRegistry) -> Self {
88 let types_by_path = registry
89 .types
90 .iter()
91 .map(|ty| (PathKey::from(&ty.ty.path), ty.id))
92 .collect::<TypesByPath>();
93 Self {
94 types_by_path,
95 encoders: HashMap::new(),
96 decoders: HashMap::new(),
97 }
98 }
99
100 pub fn with_default_custom_type_transcoders(self) -> Self {
101 self.register_custom_type_transcoder::<AccountId32, _>(env_types::AccountId)
102 .register_custom_type_transcoder::<<ink_env::DefaultEnvironment as ink_env::Environment>::AccountId, _>(env_types::AccountId)
103 .register_custom_type_transcoder::<<ink_env::DefaultEnvironment as ink_env::Environment>::Hash, _>(env_types::H256)
104 .register_custom_type_transcoder::<primitive_types::H160, _>(env_types::H160)
105 .register_custom_type_transcoder::<primitive_types::H256, _>(env_types::H256)
106 .register_custom_type_transcoder::<primitive_types::U256, _>(env_types::U256)
107 }
108
109 pub fn register_custom_type_transcoder<T, U>(self, transcoder: U) -> Self
110 where
111 T: TypeInfo + 'static,
112 U: CustomTypeEncoder + CustomTypeDecoder + Clone + 'static,
113 {
114 self.register_custom_type_encoder::<T, U>(transcoder.clone())
115 .register_custom_type_decoder::<T, U>(transcoder)
116 }
117
118 pub fn register_custom_type_encoder<T, U>(self, encoder: U) -> Self
119 where
120 T: TypeInfo + 'static,
121 U: CustomTypeEncoder + 'static,
122 {
123 let mut this = self;
124
125 let path_key = PathKey::from_type::<T>();
126 let type_id = this.types_by_path.get(&path_key);
127
128 match type_id {
129 Some(type_id) => {
130 let existing = this.encoders.insert(*type_id, Box::new(encoder));
131 tracing::debug!("Registered custom encoder for type `{:?}`", type_id);
132 if existing.is_some() {
133 panic!(
134 "Attempted to register encoder with existing type id {type_id:?}"
135 );
136 }
137 }
138 None => {
139 tracing::debug!("No matching type in registry for path {:?}.", path_key);
142 }
143 }
144 this
145 }
146
147 pub fn register_custom_type_decoder<T, U>(self, encoder: U) -> Self
148 where
149 T: TypeInfo + 'static,
150 U: CustomTypeDecoder + 'static,
151 {
152 let mut this = self;
153
154 let path_key = PathKey::from_type::<T>();
155 let type_id = this.types_by_path.get(&path_key);
156
157 match type_id {
158 Some(type_id) => {
159 let existing = this.decoders.insert(*type_id, Box::new(encoder));
160 tracing::debug!("Registered custom decoder for type `{:?}`", type_id);
161 if existing.is_some() {
162 panic!(
163 "Attempted to register decoder with existing type id {type_id:?}"
164 );
165 }
166 }
167 None => {
168 tracing::debug!("No matching type in registry for path {:?}.", path_key);
171 }
172 }
173 this
174 }
175
176 pub fn done(self) -> Transcoder {
177 let env_types_transcoder = EnvTypesTranscoder::new(self.encoders, self.decoders);
178 Transcoder::new(env_types_transcoder)
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use super::{
185 super::scon::{
186 self,
187 Hex,
188 Map,
189 Seq,
190 Tuple,
191 Value,
192 },
193 *,
194 };
195 use scale::Encode;
196 use scale_info::{
197 MetaType,
198 Registry,
199 TypeInfo,
200 };
201 use std::str::FromStr;
202
203 fn registry_with_type<T>() -> Result<(PortableRegistry, u32)>
204 where
205 T: scale_info::TypeInfo + 'static,
206 {
207 let mut registry = Registry::new();
208 let type_id = registry.register_type(&MetaType::new::<T>());
209 let registry: PortableRegistry = registry.into();
210
211 Ok((registry, type_id.id))
212 }
213
214 fn transcode_roundtrip<T>(input: &str, expected_output: Value) -> Result<()>
215 where
216 T: scale_info::TypeInfo + 'static,
217 {
218 let (registry, ty) = registry_with_type::<T>()?;
219 let transcoder = TranscoderBuilder::new(®istry)
220 .with_default_custom_type_transcoders()
221 .done();
222
223 let value = scon::parse_value(input)?;
224
225 let mut output = Vec::new();
226 transcoder.encode(®istry, ty, &value, &mut output)?;
227 let decoded = transcoder.decode(®istry, ty, &mut &output[..])?;
228 assert_eq!(expected_output, decoded, "decoding");
229 Ok(())
230 }
231
232 #[test]
233 fn transcode_bool() -> Result<()> {
234 transcode_roundtrip::<bool>("true", Value::Bool(true))?;
235 transcode_roundtrip::<bool>("false", Value::Bool(false))
236 }
237
238 #[test]
239 fn transcode_char_unsupported() -> Result<()> {
240 let (registry, ty) = registry_with_type::<char>()?;
241 let transcoder = Transcoder::new(Default::default());
242 let encoded = u32::from('c').encode();
243
244 assert!(transcoder
245 .encode(®istry, ty, &Value::Char('c'), &mut Vec::new())
246 .is_err());
247 assert!(transcoder.decode(®istry, ty, &mut &encoded[..]).is_err());
248 Ok(())
249 }
250
251 #[test]
252 fn transcode_str() -> Result<()> {
253 transcode_roundtrip::<String>("\"ink!\"", Value::String("ink!".to_string()))
254 }
255
256 #[test]
257 fn transcode_unsigned_integers() -> Result<()> {
258 transcode_roundtrip::<u8>("0", Value::UInt(0))?;
259 transcode_roundtrip::<u8>("255", Value::UInt(255))?;
260
261 transcode_roundtrip::<u16>("0", Value::UInt(0))?;
262 transcode_roundtrip::<u16>("65535", Value::UInt(65535))?;
263
264 transcode_roundtrip::<u32>("0", Value::UInt(0))?;
265 transcode_roundtrip::<u32>("4294967295", Value::UInt(4294967295))?;
266
267 transcode_roundtrip::<u64>("0", Value::UInt(0))?;
268 transcode_roundtrip::<u64>(
269 "\"18_446_744_073_709_551_615\"",
270 Value::UInt(18446744073709551615),
271 )?;
272
273 transcode_roundtrip::<u128>("0", Value::UInt(0))?;
274 transcode_roundtrip::<u128>(
275 "\"340_282_366_920_938_463_463_374_607_431_768_211_455\"",
276 Value::UInt(340282366920938463463374607431768211455),
277 )
278 }
279
280 #[test]
281 fn transcode_integers() -> Result<()> {
282 transcode_roundtrip::<i8>("-128", Value::Int(i8::MIN.into()))?;
283 transcode_roundtrip::<i8>("127", Value::Int(i8::MAX.into()))?;
284
285 transcode_roundtrip::<i16>("-32768", Value::Int(i16::MIN.into()))?;
286 transcode_roundtrip::<i16>("32767", Value::Int(i16::MAX.into()))?;
287
288 transcode_roundtrip::<i32>("-2147483648", Value::Int(i32::MIN.into()))?;
289 transcode_roundtrip::<i32>("2147483647", Value::Int(i32::MAX.into()))?;
290
291 transcode_roundtrip::<i64>("-9223372036854775808", Value::Int(i64::MIN.into()))?;
292 transcode_roundtrip::<i64>(
293 "\"9_223_372_036_854_775_807\"",
294 Value::Int(i64::MAX.into()),
295 )?;
296
297 transcode_roundtrip::<i128>(
298 "-170141183460469231731687303715884105728",
299 Value::Int(i128::MIN),
300 )?;
301 transcode_roundtrip::<i128>(
302 "\"170141183460469231731687303715884105727\"",
303 Value::Int(i128::MAX),
304 )
305 }
306
307 #[test]
308 fn transcode_byte_array() -> Result<()> {
309 transcode_roundtrip::<[u8; 2]>(
310 r#"0x0000"#,
311 Value::Seq(vec![Value::UInt(0x00), Value::UInt(0x00)].into()),
312 )?;
313 transcode_roundtrip::<[u8; 4]>(
314 r#"0xDEADBEEF"#,
315 Value::Seq(
316 vec![
317 Value::UInt(0xDE),
318 Value::UInt(0xAD),
319 Value::UInt(0xBE),
320 Value::UInt(0xEF),
321 ]
322 .into(),
323 ),
324 )?;
325 transcode_roundtrip::<[u8; 4]>(
326 r#"0xdeadbeef"#,
327 Value::Seq(
328 vec![
329 Value::UInt(0xDE),
330 Value::UInt(0xAD),
331 Value::UInt(0xBE),
332 Value::UInt(0xEF),
333 ]
334 .into(),
335 ),
336 )
337 }
338
339 #[test]
340 fn transcode_array() -> Result<()> {
341 transcode_roundtrip::<[u32; 3]>(
342 "[1, 2, 3]",
343 Value::Seq(vec![Value::UInt(1), Value::UInt(2), Value::UInt(3)].into()),
344 )?;
345 transcode_roundtrip::<[String; 2]>(
346 "[\"hello\", \"world\"]",
347 Value::Seq(
348 vec![
349 Value::String("hello".to_string()),
350 Value::String("world".to_string()),
351 ]
352 .into(),
353 ),
354 )
355 }
356
357 #[test]
358 fn transcode_seq() -> Result<()> {
359 transcode_roundtrip::<Vec<u32>>(
360 "[1, 2, 3]",
361 Value::Seq(vec![Value::UInt(1), Value::UInt(2), Value::UInt(3)].into()),
362 )?;
363 transcode_roundtrip::<Vec<String>>(
364 "[\"hello\", \"world\"]",
365 Value::Seq(
366 vec![
367 Value::String("hello".to_string()),
368 Value::String("world".to_string()),
369 ]
370 .into(),
371 ),
372 )
373 }
374
375 #[test]
376 fn transcode_tuple() -> Result<()> {
377 transcode_roundtrip::<(u32, String, [u8; 4])>(
378 r#"(1, "ink!", 0xDEADBEEF)"#,
379 Value::Tuple(Tuple::new(
380 None,
381 vec![
382 Value::UInt(1),
383 Value::String("ink!".to_string()),
384 Value::Seq(
385 vec![
386 Value::UInt(0xDE),
387 Value::UInt(0xAD),
388 Value::UInt(0xBE),
389 Value::UInt(0xEF),
390 ]
391 .into(),
392 ),
393 ],
394 )),
395 )
396 }
397
398 #[test]
399 fn transcode_composite_struct() -> Result<()> {
400 #[allow(dead_code)]
401 #[derive(TypeInfo)]
402 struct S {
403 a: u32,
404 b: String,
405 c: [u8; 4],
406 d: Vec<S>,
408 }
409
410 transcode_roundtrip::<S>(
411 r#"S(a: 1, b: "ink!", c: 0xDEADBEEF, d: [S(a: 2, b: "ink!", c: 0xDEADBEEF, d: [])])"#,
412 Value::Map(
413 vec![
414 (Value::String("a".to_string()), Value::UInt(1)),
415 (
416 Value::String("b".to_string()),
417 Value::String("ink!".to_string()),
418 ),
419 (
420 Value::String("c".to_string()),
421 Value::Seq(
422 vec![
423 Value::UInt(0xDE),
424 Value::UInt(0xAD),
425 Value::UInt(0xBE),
426 Value::UInt(0xEF),
427 ]
428 .into(),
429 ),
430 ),
431 (
432 Value::String("d".to_string()),
433 Value::Seq(
434 vec![Value::Map(
435 vec![
436 (Value::String("a".to_string()), Value::UInt(2)),
437 (
438 Value::String("b".to_string()),
439 Value::String("ink!".to_string()),
440 ),
441 (
442 Value::String("c".to_string()),
443 Value::Seq(
444 vec![
445 Value::UInt(0xDE),
446 Value::UInt(0xAD),
447 Value::UInt(0xBE),
448 Value::UInt(0xEF),
449 ]
450 .into(),
451 ),
452 ),
453 (
454 Value::String("d".to_string()),
455 Value::Seq(
456 Vec::new()
457 .into_iter()
458 .collect::<Vec<_>>()
459 .into(),
460 ),
461 ),
462 ]
463 .into_iter()
464 .collect(),
465 )]
466 .into(),
467 ),
468 ),
469 ]
470 .into_iter()
471 .collect(),
472 ),
473 )
474 }
475
476 #[test]
477 fn transcode_composite_struct_nested() -> Result<()> {
478 #[allow(dead_code)]
479 #[derive(TypeInfo)]
480 struct S {
481 nested: Nested,
482 }
483
484 #[allow(dead_code)]
485 #[derive(TypeInfo)]
486 struct Nested(u32);
487
488 transcode_roundtrip::<S>(
489 r#"S { nested: Nested(33) }"#,
490 Value::Map(Map::new(
491 Some("S"),
492 vec![(
493 Value::String("nested".to_string()),
494 Value::Tuple(Tuple::new(
495 Some("Nested"),
496 vec![Value::UInt(33)].into_iter().collect(),
497 )),
498 )]
499 .into_iter()
500 .collect(),
501 )),
502 )
503 }
504
505 #[test]
506 fn transcode_composite_struct_out_of_order_fields() -> Result<()> {
507 #[allow(dead_code)]
508 #[derive(TypeInfo)]
509 struct S {
510 a: u32,
511 b: String,
512 c: [u8; 4],
513 }
514
515 transcode_roundtrip::<S>(
516 r#"S(b: "ink!", a: 1, c: 0xDEADBEEF)"#,
517 Value::Map(
518 vec![
519 (Value::String("a".to_string()), Value::UInt(1)),
520 (
521 Value::String("b".to_string()),
522 Value::String("ink!".to_string()),
523 ),
524 (
525 Value::String("c".to_string()),
526 Value::Seq(
527 vec![
528 Value::UInt(0xDE),
529 Value::UInt(0xAD),
530 Value::UInt(0xBE),
531 Value::UInt(0xEF),
532 ]
533 .into(),
534 ),
535 ),
536 ]
537 .into_iter()
538 .collect(),
539 ),
540 )
541 }
542
543 #[test]
544 fn transcode_composite_tuple_struct() -> Result<()> {
545 #[allow(dead_code)]
546 #[derive(TypeInfo)]
547 struct S(u32, String, [u8; 4]);
548
549 transcode_roundtrip::<S>(
550 r#"S(1, "ink!", 0xDEADBEEF)"#,
551 Value::Tuple(Tuple::new(
552 Some("S"),
553 vec![
554 Value::UInt(1),
555 Value::String("ink!".to_string()),
556 Value::Seq(
557 vec![
558 Value::UInt(0xDE),
559 Value::UInt(0xAD),
560 Value::UInt(0xBE),
561 Value::UInt(0xEF),
562 ]
563 .into(),
564 ),
565 ],
566 )),
567 )
568 }
569
570 #[test]
571 fn transcode_composite_single_field_struct() -> Result<()> {
572 #[allow(dead_code)]
573 #[derive(TypeInfo)]
574 struct S([u8; 4]);
575
576 transcode_roundtrip::<S>(
577 r#"0xDEADBEEF"#,
578 Value::Tuple(Tuple::new(
579 Some("S"),
580 vec![Value::Seq(
581 vec![
582 Value::UInt(0xDE),
583 Value::UInt(0xAD),
584 Value::UInt(0xBE),
585 Value::UInt(0xEF),
586 ]
587 .into(),
588 )],
589 )),
590 )
591 }
592
593 #[test]
594 fn transcode_composite_single_field_tuple() -> Result<()> {
595 transcode_roundtrip::<([u8; 4],)>(
596 r#"0xDEADBEEF"#,
597 Value::Tuple(Tuple::new(
598 None,
599 vec![Value::Seq(
600 vec![
601 Value::UInt(0xDE),
602 Value::UInt(0xAD),
603 Value::UInt(0xBE),
604 Value::UInt(0xEF),
605 ]
606 .into(),
607 )],
608 )),
609 )
610 }
611
612 #[test]
613 fn transcode_enum_variant_tuple() -> Result<()> {
614 #[derive(TypeInfo)]
615 #[allow(dead_code)]
616 enum E {
617 A(u32, String),
618 B { a: [u8; 4], b: Vec<E> },
619 C,
620 }
621
622 transcode_roundtrip::<E>(
623 r#"A(1, "2")"#,
624 Value::Tuple(Tuple::new(
625 Some("A"),
626 vec![Value::UInt(1), Value::String("2".into())],
627 )),
628 )
629 }
630
631 #[test]
632 fn transcode_enum_variant_map() -> Result<()> {
633 #[derive(TypeInfo)]
634 #[allow(dead_code)]
635 enum E {
636 A { a: u32, b: bool },
637 }
638
639 transcode_roundtrip::<E>(
640 r#"A { a: 33, b: false }"#,
641 Value::Map(Map::new(
642 Some("A"),
643 vec![
644 (Value::String("a".to_string()), Value::UInt(33)),
645 (Value::String("b".to_string()), Value::Bool(false)),
646 ]
647 .into_iter()
648 .collect(),
649 )),
650 )
651 }
652
653 #[test]
654 fn transcode_enum_variant_map_out_of_order_fields() -> Result<()> {
655 #[derive(TypeInfo)]
656 #[allow(dead_code)]
657 enum E {
658 A { a: u32, b: bool },
659 }
660
661 transcode_roundtrip::<E>(
662 r#"A { a: 33, b: false }"#,
663 Value::Map(Map::new(
664 Some("A"),
665 vec![
666 (Value::String("a".to_string()), Value::UInt(33)),
667 (Value::String("b".to_string()), Value::Bool(false)),
668 ]
669 .into_iter()
670 .collect(),
671 )),
672 )
673 }
674
675 #[test]
676 fn transcode_option() -> Result<()> {
677 transcode_roundtrip::<Option<u32>>(
678 r#"Some(32)"#,
679 Value::Tuple(Tuple::new(Some("Some"), vec![Value::UInt(32)])),
680 )?;
681
682 transcode_roundtrip::<Option<u32>>(
683 r#"None"#,
684 Value::Tuple(Tuple::new(Some("None"), Vec::new())),
685 )
686 }
687
688 #[test]
689 fn transcode_account_id_custom_ss58_encoding() -> Result<()> {
690 type AccountId = AccountId32;
691
692 #[allow(dead_code)]
693 #[derive(TypeInfo)]
694 struct S {
695 no_alias: AccountId32,
696 aliased: AccountId,
697 }
698
699 transcode_roundtrip::<S>(
700 r#"S(
701 no_alias: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,
702 aliased: 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty,
703 )"#,
704 Value::Map(Map::new(
705 Some("S"),
706 vec![
707 (
708 Value::String("no_alias".into()),
709 Value::Literal(
710 "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY".into(),
711 ),
712 ),
713 (
714 Value::String("aliased".into()),
715 Value::Literal(
716 "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty".into(),
717 ),
718 ),
719 ]
720 .into_iter()
721 .collect(),
722 )),
723 )
724 }
725
726 #[test]
727 fn transcode_account_id_custom_ss58_encoding_seq() -> Result<()> {
728 let hex_to_bytes = |hex: &str| -> Result<Value> {
729 let hex = Hex::from_str(hex)?;
730 let values = hex.bytes().iter().map(|b| Value::UInt((*b).into()));
731 Ok(Value::Seq(Seq::new(values.collect())))
732 };
733
734 transcode_roundtrip::<Vec<AccountId32>>(
735 r#"[
736 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,
737 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty,
738 ]"#,
739 Value::Seq(Seq::new(
740 vec![
741 Value::Tuple(
742 Tuple::new(
743 Some("AccountId32"),
744 vec![hex_to_bytes("0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d")?]
745 )
746 ),
747 Value::Tuple(
748 Tuple::new(
749 Some("AccountId32"),
750 vec![hex_to_bytes("0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48")?]
751 )
752 )
753 ]
754 .into_iter()
755 .collect(),
756 )),
757 )
758 }
759
760 #[test]
761 fn decode_h256_as_hex_string() -> Result<()> {
762 #[allow(dead_code)]
763 #[derive(TypeInfo)]
764 struct S {
765 hash: primitive_types::H256,
766 }
767
768 transcode_roundtrip::<S>(
769 r#"S(
770 hash: 0x3428ebe146f5b82415da82724bcf75f053768738dcac5f83ab7d82a70a0ff2de,
771 )"#,
772 Value::Map(Map::new(
773 Some("S"),
774 vec![
775 (
776 Value::String("hash".into()),
777 Value::Hex(
778 Hex::from_str("0x3428ebe146f5b82415da82724bcf75f053768738dcac5f83ab7d82a70a0ff2de")?,
779 ),
780 ),
781 ]
782 .into_iter()
783 .collect(),
784 )),
785 )
786 }
787
788 #[test]
789 fn transcode_compact_primitives() -> Result<()> {
790 transcode_roundtrip::<scale::Compact<u8>>(r#"33"#, Value::UInt(33))?;
791
792 transcode_roundtrip::<scale::Compact<u16>>(r#"33"#, Value::UInt(33))?;
793
794 transcode_roundtrip::<scale::Compact<u32>>(r#"33"#, Value::UInt(33))?;
795
796 transcode_roundtrip::<scale::Compact<u64>>(r#"33"#, Value::UInt(33))?;
797
798 transcode_roundtrip::<scale::Compact<u128>>(r#"33"#, Value::UInt(33))
799 }
800
801 #[test]
802 fn transcode_compact_struct() -> Result<()> {
803 #[derive(scale::Encode, scale::CompactAs, TypeInfo)]
804 struct CompactStruct(u32);
805
806 #[allow(dead_code)]
807 #[derive(scale::Encode, TypeInfo)]
808 struct S {
809 #[codec(compact)]
810 a: CompactStruct,
811 }
812
813 transcode_roundtrip::<S>(
814 r#"S { a: CompactStruct(33) }"#,
815 Value::Map(Map::new(
816 Some("S"),
817 vec![(
818 Value::String("a".to_string()),
819 Value::Tuple(Tuple::new(
820 Some("CompactStruct"),
821 vec![Value::UInt(33)],
822 )),
823 )]
824 .into_iter()
825 .collect(),
826 )),
827 )
828 }
829
830 #[test]
831 fn transcode_hex_literal_uints() -> Result<()> {
832 #[derive(scale::Encode, TypeInfo)]
833 struct S(u8, u16, u32, u64, u128);
834
835 transcode_roundtrip::<S>(
836 r#"S (0xDE, 0xDEAD, 0xDEADBEEF, 0xDEADBEEF12345678, 0xDEADBEEF0123456789ABCDEF01234567)"#,
837 Value::Tuple(Tuple::new(
838 Some("S"),
839 vec![
840 Value::UInt(0xDE),
841 Value::UInt(0xDEAD),
842 Value::UInt(0xDEADBEEF),
843 Value::UInt(0xDEADBEEF12345678),
844 Value::UInt(0xDEADBEEF0123456789ABCDEF01234567),
845 ]
846 .into_iter()
847 .collect(),
848 )),
849 )
850 }
851}