1use crate::metadata::ContractMetadataKind;
2use ab_io_type::metadata::{IoTypeDetails, IoTypeMetadataKind};
3use core::mem::ManuallyDrop;
4use derive_destructure2::destructure;
5
6#[derive(Debug, thiserror::Error)]
8pub enum MetadataDecodingError<'metadata> {
9 #[error("Not enough metadata to decode")]
11 NotEnoughMetadata,
12 #[error("Invalid first metadata byte")]
14 InvalidFirstMetadataByte { byte: u8 },
15 #[error("Multiple contracts found")]
17 MultipleContractsFound,
18 #[error("Expected contract or trait kind, found something else: {metadata_kind:?}")]
20 ExpectedContractOrTrait { metadata_kind: ContractMetadataKind },
21 #[error("Failed to decode state type name")]
23 FailedToDecodeStateTypeName,
24 #[error("Invalid state I/O type")]
26 InvalidStateIoType,
27 #[error("Unexpected method kind {method_kind:?} for container kind {container_kind:?}")]
29 UnexpectedMethodKind {
30 method_kind: MethodKind,
31 container_kind: MethodsContainerKind,
32 },
33 #[error("Expected method kind, found something else: {metadata_kind:?}")]
35 ExpectedMethodKind { metadata_kind: ContractMetadataKind },
36 #[error("Expected argument kind, found something else: {metadata_kind:?}")]
38 ExpectedArgumentKind { metadata_kind: ContractMetadataKind },
39 #[error("Unexpected argument kind {argument_kind:?} for method kind {method_kind:?}")]
41 UnexpectedArgumentKind {
42 argument_kind: ArgumentKind,
43 method_kind: MethodKind,
44 },
45 #[error("Invalid argument I/O type of kind {argument_kind:?} for {argument_name:?}")]
47 InvalidArgumentIoType {
48 argument_name: &'metadata [u8],
49 argument_kind: ArgumentKind,
50 },
51}
52
53#[derive(Debug)]
54pub enum MetadataItem<'a, 'metadata> {
55 Contract {
56 state_type_name: &'metadata [u8],
61 state_type_details: IoTypeDetails,
62 slot_type_details: IoTypeDetails,
63 tmp_type_details: IoTypeDetails,
64 num_methods: u8,
65 decoder: MethodsMetadataDecoder<'a, 'metadata>,
66 },
67 Trait {
68 trait_name: &'metadata [u8],
73 num_methods: u8,
74 decoder: MethodsMetadataDecoder<'a, 'metadata>,
75 },
76}
77
78impl<'a, 'metadata> MetadataItem<'a, 'metadata> {
79 #[inline(always)]
80 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
81 pub fn num_methods(&self) -> u8 {
82 match self {
83 MetadataItem::Contract { num_methods, .. }
84 | MetadataItem::Trait { num_methods, .. } => *num_methods,
85 }
86 }
87
88 #[inline(always)]
89 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
90 pub fn into_decoder(self) -> MethodsMetadataDecoder<'a, 'metadata> {
91 match self {
92 MetadataItem::Contract { decoder, .. } | MetadataItem::Trait { decoder, .. } => decoder,
93 }
94 }
95}
96
97#[derive(Debug)]
98pub struct MetadataDecoder<'metadata> {
99 metadata: &'metadata [u8],
100 found_contract: bool,
101 found_something: bool,
102}
103
104impl<'metadata> MetadataDecoder<'metadata> {
105 #[inline(always)]
106 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
107 pub fn new(metadata: &'metadata [u8]) -> Self {
108 Self {
109 metadata,
110 found_contract: false,
111 found_something: false,
112 }
113 }
114
115 #[inline(always)]
117 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
118 pub fn remaining_metadata_bytes(&self) -> usize {
119 self.metadata.len()
120 }
121
122 #[inline]
124 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
125 pub fn decode_next<'a>(
126 &'a mut self,
127 ) -> Option<Result<MetadataItem<'a, 'metadata>, MetadataDecodingError<'metadata>>> {
128 let metadata_kind = *self.metadata.split_off_first()?;
130 let Some(metadata_kind) = ContractMetadataKind::try_from_u8(metadata_kind) else {
131 return Some(Err(MetadataDecodingError::InvalidFirstMetadataByte {
132 byte: metadata_kind,
133 }));
134 };
135
136 self.found_something = true;
137
138 match metadata_kind {
139 ContractMetadataKind::Contract => {
140 if self.found_contract {
141 return Some(Err(MetadataDecodingError::MultipleContractsFound));
142 }
143 self.found_contract = true;
144
145 Some(self.decode_contract())
146 }
147 ContractMetadataKind::Trait => Some(self.decode_trait()),
148 ContractMetadataKind::Init
150 | ContractMetadataKind::UpdateStateless
151 | ContractMetadataKind::UpdateStatefulRo
152 | ContractMetadataKind::UpdateStatefulRw
153 | ContractMetadataKind::ViewStateless
154 | ContractMetadataKind::ViewStateful
155 | ContractMetadataKind::EnvRo
156 | ContractMetadataKind::EnvRw
157 | ContractMetadataKind::TmpRo
158 | ContractMetadataKind::TmpRw
159 | ContractMetadataKind::SlotRo
160 | ContractMetadataKind::SlotRw
161 | ContractMetadataKind::Input
162 | ContractMetadataKind::Output
163 | ContractMetadataKind::Return => {
164 Some(Err(MetadataDecodingError::ExpectedContractOrTrait {
165 metadata_kind,
166 }))
167 }
168 }
169 }
170
171 #[inline(always)]
172 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
173 fn decode_contract<'a>(
174 &'a mut self,
175 ) -> Result<MetadataItem<'a, 'metadata>, MetadataDecodingError<'metadata>> {
176 let state_type_name = IoTypeMetadataKind::type_name(self.metadata)
178 .ok_or(MetadataDecodingError::FailedToDecodeStateTypeName)?;
179
180 let state_type_details;
182 (state_type_details, self.metadata) = IoTypeMetadataKind::type_details(self.metadata)
183 .ok_or(MetadataDecodingError::InvalidStateIoType)?;
184
185 let slot_type_details;
187 (slot_type_details, self.metadata) = IoTypeMetadataKind::type_details(self.metadata)
188 .ok_or(MetadataDecodingError::InvalidStateIoType)?;
189
190 let tmp_type_details;
192 (tmp_type_details, self.metadata) = IoTypeMetadataKind::type_details(self.metadata)
193 .ok_or(MetadataDecodingError::InvalidStateIoType)?;
194
195 let num_methods = *self
197 .metadata
198 .split_off_first()
199 .ok_or(MetadataDecodingError::NotEnoughMetadata)?;
200
201 Ok(MetadataItem::Contract {
202 state_type_name,
203 state_type_details,
204 slot_type_details,
205 tmp_type_details,
206 num_methods,
207 decoder: MethodsMetadataDecoder::new(
208 &mut self.metadata,
209 MethodsContainerKind::Contract,
210 num_methods,
211 ),
212 })
213 }
214
215 #[inline(always)]
216 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
217 fn decode_trait<'a>(
218 &'a mut self,
219 ) -> Result<MetadataItem<'a, 'metadata>, MetadataDecodingError<'metadata>> {
220 let trait_name_length = usize::from(
222 *self
223 .metadata
224 .split_off_first()
225 .ok_or(MetadataDecodingError::NotEnoughMetadata)?,
226 );
227
228 let trait_name = self
229 .metadata
230 .split_off(..trait_name_length)
231 .ok_or(MetadataDecodingError::NotEnoughMetadata)?;
232
233 let num_methods = *self
235 .metadata
236 .split_off_first()
237 .ok_or(MetadataDecodingError::NotEnoughMetadata)?;
238
239 Ok(MetadataItem::Trait {
240 trait_name,
241 num_methods,
242 decoder: MethodsMetadataDecoder::new(
243 &mut self.metadata,
244 MethodsContainerKind::Trait,
245 num_methods,
246 ),
247 })
248 }
249}
250
251#[derive(Debug, Copy, Clone)]
252pub enum MethodsContainerKind {
253 Contract,
254 Trait,
255 Unknown,
256}
257
258#[derive(Debug)]
259pub struct MethodsMetadataDecoder<'a, 'metadata> {
260 metadata: &'a mut &'metadata [u8],
261 container_kind: MethodsContainerKind,
262 remaining: u8,
263}
264
265impl<'a, 'metadata> Drop for MethodsMetadataDecoder<'a, 'metadata> {
266 fn drop(&mut self) {
267 while let Some(_method_metadata_decoder) = self.decode_next() {
268 }
270 }
271}
272
273impl<'a, 'metadata> MethodsMetadataDecoder<'a, 'metadata> {
274 #[inline(always)]
275 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
276 fn new(
277 metadata: &'a mut &'metadata [u8],
278 container_kind: MethodsContainerKind,
279 num_methods: u8,
280 ) -> Self {
281 Self {
282 metadata,
283 container_kind,
284 remaining: num_methods,
285 }
286 }
287
288 #[inline(always)]
290 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
291 pub fn remaining_methods(&self) -> u8 {
292 self.remaining
293 }
294
295 #[inline(always)]
297 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
298 pub fn remaining_metadata_bytes(&self) -> usize {
299 self.metadata.len()
300 }
301
302 #[inline(always)]
303 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
304 pub fn decode_next<'b>(&'b mut self) -> Option<MethodMetadataDecoder<'b, 'metadata>> {
305 if self.remaining == 0 {
306 return None;
307 }
308
309 self.remaining -= 1;
310
311 Some(MethodMetadataDecoder::new(
312 self.metadata,
313 self.container_kind,
314 ))
315 }
316}
317
318#[derive(Debug, Copy, Clone)]
319pub enum MethodKind {
320 Init,
322 UpdateStateless,
324 UpdateStatefulRo,
326 UpdateStatefulRw,
328 ViewStateless,
330 ViewStateful,
332}
333
334impl MethodKind {
335 #[inline(always)]
336 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
337 pub fn has_self(&self) -> bool {
338 match self {
339 MethodKind::Init | MethodKind::UpdateStateless | MethodKind::ViewStateless => false,
340 MethodKind::UpdateStatefulRo
341 | MethodKind::UpdateStatefulRw
342 | MethodKind::ViewStateful => true,
343 }
344 }
345}
346
347#[derive(Debug, Copy, Clone)]
348pub struct MethodMetadataItem<'metadata> {
349 pub method_name: &'metadata [u8],
353 pub method_kind: MethodKind,
354 pub num_arguments: u8,
355}
356
357#[derive(Debug, destructure)]
359pub struct MethodMetadataDecoder<'a, 'metadata> {
360 metadata: &'a mut &'metadata [u8],
361 container_kind: MethodsContainerKind,
362}
363
364impl<'a, 'metadata> Drop for MethodMetadataDecoder<'a, 'metadata> {
365 fn drop(&mut self) {
366 let metadata_before = *self.metadata;
367
368 if MethodMetadataDecoder::new(self.metadata, self.container_kind)
370 .decode_next()
371 .is_err()
372 {
373 *self.metadata = metadata_before;
376 }
377 }
378}
379
380impl<'a, 'metadata> MethodMetadataDecoder<'a, 'metadata> {
381 #[inline(always)]
382 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
383 pub fn new(metadata: &'a mut &'metadata [u8], container_kind: MethodsContainerKind) -> Self {
384 Self {
385 metadata,
386 container_kind,
387 }
388 }
389
390 #[inline(always)]
392 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
393 pub fn remaining_metadata_bytes(&self) -> usize {
394 self.metadata.len()
395 }
396
397 #[inline]
398 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
399 pub fn decode_next(
400 self,
401 ) -> Result<
402 (
403 ArgumentsMetadataDecoder<'a, 'metadata>,
404 MethodMetadataItem<'metadata>,
405 ),
406 MetadataDecodingError<'metadata>,
407 > {
408 let (metadata, container_kind) = self.destructure();
409
410 let metadata_kind = *metadata
412 .split_off_first()
413 .ok_or(MetadataDecodingError::NotEnoughMetadata)?;
414 let metadata_kind = ContractMetadataKind::try_from_u8(metadata_kind).ok_or(
415 MetadataDecodingError::InvalidFirstMetadataByte {
416 byte: metadata_kind,
417 },
418 )?;
419
420 let method_kind = match metadata_kind {
421 ContractMetadataKind::Init => MethodKind::Init,
422 ContractMetadataKind::UpdateStateless => MethodKind::UpdateStateless,
423 ContractMetadataKind::UpdateStatefulRo => MethodKind::UpdateStatefulRo,
424 ContractMetadataKind::UpdateStatefulRw => MethodKind::UpdateStatefulRw,
425 ContractMetadataKind::ViewStateless => MethodKind::ViewStateless,
426 ContractMetadataKind::ViewStateful => MethodKind::ViewStateful,
427 ContractMetadataKind::Contract
429 | ContractMetadataKind::Trait
430 | ContractMetadataKind::EnvRo
431 | ContractMetadataKind::EnvRw
432 | ContractMetadataKind::TmpRo
433 | ContractMetadataKind::TmpRw
434 | ContractMetadataKind::SlotRo
435 | ContractMetadataKind::SlotRw
436 | ContractMetadataKind::Input
437 | ContractMetadataKind::Output
438 | ContractMetadataKind::Return => {
439 return Err(MetadataDecodingError::ExpectedMethodKind { metadata_kind });
440 }
441 };
442
443 let method_allowed = match container_kind {
444 MethodsContainerKind::Contract | MethodsContainerKind::Unknown => match method_kind {
445 MethodKind::Init
446 | MethodKind::UpdateStateless
447 | MethodKind::UpdateStatefulRo
448 | MethodKind::UpdateStatefulRw
449 | MethodKind::ViewStateless
450 | MethodKind::ViewStateful => true,
451 },
452 MethodsContainerKind::Trait => match method_kind {
453 MethodKind::Init
454 | MethodKind::UpdateStatefulRo
455 | MethodKind::UpdateStatefulRw
456 | MethodKind::ViewStateful => false,
457 MethodKind::UpdateStateless | MethodKind::ViewStateless => true,
458 },
459 };
460
461 if !method_allowed {
462 return Err(MetadataDecodingError::UnexpectedMethodKind {
463 method_kind,
464 container_kind,
465 });
466 }
467
468 let method_name_length = usize::from(
470 *metadata
471 .split_off_first()
472 .ok_or(MetadataDecodingError::NotEnoughMetadata)?,
473 );
474
475 let method_name = metadata
476 .split_off(..method_name_length)
477 .ok_or(MetadataDecodingError::NotEnoughMetadata)?;
478
479 let num_arguments = *metadata
481 .split_off_first()
482 .ok_or(MetadataDecodingError::NotEnoughMetadata)?;
483
484 let decoder = ArgumentsMetadataDecoder {
485 metadata,
486 method_kind,
487 remaining: num_arguments,
488 };
489 let item = MethodMetadataItem {
490 method_name,
491 method_kind,
492 num_arguments,
493 };
494
495 Ok((decoder, item))
496 }
497}
498
499#[derive(Debug, Copy, Clone)]
500pub enum ArgumentKind {
501 EnvRo,
503 EnvRw,
505 TmpRo,
507 TmpRw,
509 SlotRo,
511 SlotRw,
513 Input,
515 Output,
517 Return,
519}
520
521#[derive(Debug)]
522pub struct ArgumentMetadataItem<'metadata> {
523 pub argument_name: &'metadata [u8],
527 pub argument_kind: ArgumentKind,
528 pub type_details: Option<IoTypeDetails>,
533}
534
535#[derive(Debug)]
536pub struct ArgumentsMetadataDecoder<'a, 'metadata> {
537 metadata: &'a mut &'metadata [u8],
538 method_kind: MethodKind,
539 remaining: u8,
540}
541
542impl<'a, 'metadata> Drop for ArgumentsMetadataDecoder<'a, 'metadata> {
543 fn drop(&mut self) {
544 let metadata_before = *self.metadata;
545 while let Some(maybe_argument_metadata_item) = self.decode_next() {
546 if maybe_argument_metadata_item.is_err() {
547 *self.metadata = metadata_before;
550 break;
551 }
552 }
553 }
554}
555
556impl<'metadata> ArgumentsMetadataDecoder<'_, 'metadata> {
557 pub fn without_auto_drain(self) -> ManuallyDrop<Self> {
567 ManuallyDrop::new(self)
568 }
569
570 #[inline(always)]
572 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
573 pub fn remaining_metadata_bytes(&self) -> usize {
574 self.metadata.len()
575 }
576
577 #[inline]
578 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
579 pub fn decode_next<'a>(
580 &'a mut self,
581 ) -> Option<Result<ArgumentMetadataItem<'metadata>, MetadataDecodingError<'metadata>>> {
582 if self.remaining == 0 {
583 return None;
584 }
585
586 self.remaining -= 1;
587
588 Some(self.decode_argument())
589 }
590
591 #[inline(always)]
592 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
593 fn decode_argument<'a>(
594 &'a mut self,
595 ) -> Result<ArgumentMetadataItem<'metadata>, MetadataDecodingError<'metadata>> {
596 let metadata_kind = *self
598 .metadata
599 .split_off_first()
600 .ok_or(MetadataDecodingError::NotEnoughMetadata)?;
601 let metadata_kind = ContractMetadataKind::try_from_u8(metadata_kind).ok_or(
602 MetadataDecodingError::InvalidFirstMetadataByte {
603 byte: metadata_kind,
604 },
605 )?;
606
607 let argument_kind = match metadata_kind {
608 ContractMetadataKind::EnvRo => ArgumentKind::EnvRo,
609 ContractMetadataKind::EnvRw => ArgumentKind::EnvRw,
610 ContractMetadataKind::TmpRo => ArgumentKind::TmpRo,
611 ContractMetadataKind::TmpRw => ArgumentKind::TmpRw,
612 ContractMetadataKind::SlotRo => ArgumentKind::SlotRo,
613 ContractMetadataKind::SlotRw => ArgumentKind::SlotRw,
614 ContractMetadataKind::Input => ArgumentKind::Input,
615 ContractMetadataKind::Output => ArgumentKind::Output,
616 ContractMetadataKind::Return => ArgumentKind::Return,
617 ContractMetadataKind::Contract
619 | ContractMetadataKind::Trait
620 | ContractMetadataKind::Init
621 | ContractMetadataKind::UpdateStateless
622 | ContractMetadataKind::UpdateStatefulRo
623 | ContractMetadataKind::UpdateStatefulRw
624 | ContractMetadataKind::ViewStateless
625 | ContractMetadataKind::ViewStateful => {
626 return Err(MetadataDecodingError::ExpectedArgumentKind { metadata_kind });
627 }
628 };
629
630 let argument_allowed = match self.method_kind {
632 MethodKind::Init
633 | MethodKind::UpdateStateless
634 | MethodKind::UpdateStatefulRo
635 | MethodKind::UpdateStatefulRw => match argument_kind {
636 ArgumentKind::EnvRo
637 | ArgumentKind::EnvRw
638 | ArgumentKind::TmpRo
639 | ArgumentKind::TmpRw
640 | ArgumentKind::SlotRo
641 | ArgumentKind::SlotRw
642 | ArgumentKind::Input
643 | ArgumentKind::Output
644 | ArgumentKind::Return => true,
645 },
646 MethodKind::ViewStateless | MethodKind::ViewStateful => match argument_kind {
647 ArgumentKind::EnvRo
648 | ArgumentKind::SlotRo
649 | ArgumentKind::Input
650 | ArgumentKind::Output
651 | ArgumentKind::Return => true,
652 ArgumentKind::EnvRw
653 | ArgumentKind::TmpRo
654 | ArgumentKind::TmpRw
655 | ArgumentKind::SlotRw => false,
656 },
657 };
658
659 if !argument_allowed {
660 return Err(MetadataDecodingError::UnexpectedArgumentKind {
661 argument_kind,
662 method_kind: self.method_kind,
663 });
664 }
665
666 let (argument_name, type_details) = match argument_kind {
667 ArgumentKind::EnvRo | ArgumentKind::EnvRw => ("env".as_bytes(), None),
668 ArgumentKind::TmpRo
669 | ArgumentKind::TmpRw
670 | ArgumentKind::SlotRo
671 | ArgumentKind::SlotRw
672 | ArgumentKind::Input
673 | ArgumentKind::Output
674 | ArgumentKind::Return => {
675 let argument_name_length = usize::from(
677 *self
678 .metadata
679 .split_off_first()
680 .ok_or(MetadataDecodingError::NotEnoughMetadata)?,
681 );
682 let argument_name = self
683 .metadata
684 .split_off(..argument_name_length)
685 .ok_or(MetadataDecodingError::NotEnoughMetadata)?;
686
687 let recommended_capacity = match argument_kind {
688 ArgumentKind::EnvRo
689 | ArgumentKind::EnvRw
690 | ArgumentKind::TmpRo
691 | ArgumentKind::TmpRw
692 | ArgumentKind::SlotRo
693 | ArgumentKind::SlotRw => None,
694 ArgumentKind::Input => {
695 let recommended_capacity;
696 (recommended_capacity, *self.metadata) =
697 IoTypeMetadataKind::type_details(self.metadata).ok_or(
698 MetadataDecodingError::InvalidArgumentIoType {
699 argument_name,
700 argument_kind,
701 },
702 )?;
703
704 Some(recommended_capacity)
705 }
706 ArgumentKind::Output | ArgumentKind::Return => {
707 let last_argument = self.remaining == 0;
708 if matches!((self.method_kind, last_argument), (MethodKind::Init, true)) {
711 None
712 } else {
713 let recommended_capacity;
714 (recommended_capacity, *self.metadata) =
715 IoTypeMetadataKind::type_details(self.metadata).ok_or(
716 MetadataDecodingError::InvalidArgumentIoType {
717 argument_name,
718 argument_kind,
719 },
720 )?;
721
722 Some(recommended_capacity)
723 }
724 }
725 };
726
727 (argument_name, recommended_capacity)
728 }
729 };
730
731 Ok(ArgumentMetadataItem {
732 argument_name,
733 argument_kind,
734 type_details,
735 })
736 }
737}