1use crate::metadata::ContractMetadataKind;
2use ab_contracts_io_type::metadata::{IoTypeDetails, IoTypeMetadataKind};
3use core::str::Utf8Error;
4
5#[derive(Debug, thiserror::Error)]
7pub enum MetadataDecodingError<'metadata> {
8 #[error("Not enough metadata to decode")]
10 NotEnoughMetadata,
11 #[error("Invalid first metadata byte")]
13 InvalidFirstMetadataByte { byte: u8 },
14 #[error("Multiple contracts found")]
16 MultipleContractsFound,
17 #[error("Expected contract or trait kind, found something else: {metadata_kind:?}")]
19 ExpectedContractOrTrait { metadata_kind: ContractMetadataKind },
20 #[error("Failed to decode state type name")]
22 FailedToDecodeStateTypeName,
23 #[error("Invalid state I/O type")]
25 InvalidStateIoType,
26 #[error("Invalid trait name {trait_name:?}: {error}")]
28 InvalidTraitName {
29 trait_name: &'metadata [u8],
30 error: Utf8Error,
31 },
32 #[error("Unexpected method kind {method_kind:?} for container kind {container_kind:?}")]
34 UnexpectedMethodKind {
35 method_kind: MethodKind,
36 container_kind: MethodsContainerKind,
37 },
38 #[error("Expected method kind, found something else: {metadata_kind:?}")]
40 ExpectedMethodKind { metadata_kind: ContractMetadataKind },
41 #[error("Invalid method name {method_name:?}: {error}")]
43 InvalidMethodName {
44 method_name: &'metadata [u8],
45 error: Utf8Error,
46 },
47 #[error("Expected argument kind, found something else: {metadata_kind:?}")]
49 ExpectedArgumentKind { metadata_kind: ContractMetadataKind },
50 #[error("Unexpected argument kind {argument_kind:?} for method kind {method_kind:?}")]
52 UnexpectedArgumentKind {
53 argument_kind: ArgumentKind,
54 method_kind: MethodKind,
55 },
56 #[error("Invalid argument name {argument_name:?}: {error}")]
58 InvalidArgumentName {
59 argument_name: &'metadata [u8],
60 error: Utf8Error,
61 },
62 #[error("Invalid argument I/O type of kind {argument_kind:?} for {argument_name}")]
64 InvalidArgumentIoType {
65 argument_name: &'metadata str,
66 argument_kind: ArgumentKind,
67 },
68}
69
70#[derive(Debug)]
71pub enum MetadataItem<'a, 'metadata> {
72 Contract {
73 state_type_name: &'metadata str,
74 state_type_details: IoTypeDetails,
75 slot_type_details: IoTypeDetails,
76 tmp_type_details: IoTypeDetails,
77 num_methods: u8,
78 decoder: MethodsMetadataDecoder<'a, 'metadata>,
79 },
80 Trait {
81 trait_name: &'metadata str,
82 num_methods: u8,
83 decoder: MethodsMetadataDecoder<'a, 'metadata>,
84 },
85}
86
87impl<'a, 'metadata> MetadataItem<'a, 'metadata> {
88 #[inline(always)]
89 pub fn num_methods(&self) -> u8 {
90 match self {
91 MetadataItem::Contract { num_methods, .. }
92 | MetadataItem::Trait { num_methods, .. } => *num_methods,
93 }
94 }
95
96 #[inline(always)]
97 pub fn into_decoder(self) -> MethodsMetadataDecoder<'a, 'metadata> {
98 match self {
99 MetadataItem::Contract { decoder, .. } | MetadataItem::Trait { decoder, .. } => decoder,
100 }
101 }
102}
103
104#[derive(Debug)]
105pub struct MetadataDecoder<'metadata> {
106 metadata: &'metadata [u8],
107 found_contract: bool,
108 found_something: bool,
109}
110
111impl<'metadata> MetadataDecoder<'metadata> {
112 #[inline(always)]
113 pub fn new(metadata: &'metadata [u8]) -> Self {
114 Self {
115 metadata,
116 found_contract: false,
117 found_something: false,
118 }
119 }
120
121 #[inline(always)]
122 pub fn decode_next<'a>(
123 &'a mut self,
124 ) -> Option<Result<MetadataItem<'a, 'metadata>, MetadataDecodingError<'metadata>>> {
125 if self.metadata.is_empty() {
126 return Some(Err(MetadataDecodingError::NotEnoughMetadata));
127 }
128
129 let Some(metadata_kind) = ContractMetadataKind::try_from_u8(self.metadata[0]) else {
131 return Some(Err(MetadataDecodingError::InvalidFirstMetadataByte {
132 byte: self.metadata[0],
133 }));
134 };
135 self.metadata = &self.metadata[1..];
136
137 self.found_something = true;
138
139 match metadata_kind {
140 ContractMetadataKind::Contract => {
141 if self.found_contract {
142 return Some(Err(MetadataDecodingError::MultipleContractsFound));
143 }
144 self.found_contract = true;
145
146 Some(self.decode_contract())
147 }
148 ContractMetadataKind::Trait => Some(self.decode_trait()),
149 ContractMetadataKind::Init
151 | ContractMetadataKind::UpdateStateless
152 | ContractMetadataKind::UpdateStatefulRo
153 | ContractMetadataKind::UpdateStatefulRw
154 | ContractMetadataKind::ViewStateless
155 | ContractMetadataKind::ViewStateful
156 | ContractMetadataKind::EnvRo
157 | ContractMetadataKind::EnvRw
158 | ContractMetadataKind::TmpRo
159 | ContractMetadataKind::TmpRw
160 | ContractMetadataKind::SlotRo
161 | ContractMetadataKind::SlotRw
162 | ContractMetadataKind::Input
163 | ContractMetadataKind::Output => {
164 Some(Err(MetadataDecodingError::ExpectedContractOrTrait {
165 metadata_kind,
166 }))
167 }
168 }
169 }
170
171 #[inline(always)]
172 fn decode_contract<'a>(
173 &'a mut self,
174 ) -> Result<MetadataItem<'a, 'metadata>, MetadataDecodingError<'metadata>> {
175 let state_type_name = IoTypeMetadataKind::type_name(self.metadata)
177 .ok_or(MetadataDecodingError::FailedToDecodeStateTypeName)?;
178
179 let state_type_details;
181 (state_type_details, self.metadata) = IoTypeMetadataKind::type_details(self.metadata)
182 .ok_or(MetadataDecodingError::InvalidStateIoType)?;
183
184 let slot_type_details;
186 (slot_type_details, self.metadata) = IoTypeMetadataKind::type_details(self.metadata)
187 .ok_or(MetadataDecodingError::InvalidStateIoType)?;
188
189 let tmp_type_details;
191 (tmp_type_details, self.metadata) = IoTypeMetadataKind::type_details(self.metadata)
192 .ok_or(MetadataDecodingError::InvalidStateIoType)?;
193
194 let num_methods = self.metadata[0];
196 self.metadata = &self.metadata[1..];
197
198 Ok(MetadataItem::Contract {
199 state_type_name,
200 state_type_details,
201 slot_type_details,
202 tmp_type_details,
203 num_methods,
204 decoder: MethodsMetadataDecoder::new(
205 &mut self.metadata,
206 MethodsContainerKind::Contract,
207 num_methods,
208 ),
209 })
210 }
211
212 #[inline(always)]
213 fn decode_trait<'a>(
214 &'a mut self,
215 ) -> Result<MetadataItem<'a, 'metadata>, MetadataDecodingError<'metadata>> {
216 let trait_name_length = usize::from(self.metadata[0]);
218 self.metadata = &self.metadata[1..];
219
220 if self.metadata.len() < trait_name_length + 1 {
222 return Err(MetadataDecodingError::NotEnoughMetadata);
223 }
224
225 let trait_name = &self.metadata[..trait_name_length];
226 self.metadata = &self.metadata[trait_name_length..];
227 let trait_name = str::from_utf8(trait_name)
228 .map_err(|error| MetadataDecodingError::InvalidTraitName { trait_name, error })?;
229
230 let num_methods = self.metadata[0];
232 self.metadata = &self.metadata[1..];
233
234 Ok(MetadataItem::Trait {
235 trait_name,
236 num_methods,
237 decoder: MethodsMetadataDecoder::new(
238 &mut self.metadata,
239 MethodsContainerKind::Trait,
240 num_methods,
241 ),
242 })
243 }
244}
245
246#[derive(Debug, Copy, Clone)]
247pub enum MethodsContainerKind {
248 Contract,
249 Trait,
250 Unknown,
251}
252
253#[derive(Debug)]
254pub struct MethodsMetadataDecoder<'a, 'metadata> {
255 metadata: &'a mut &'metadata [u8],
256 container_kind: MethodsContainerKind,
257 remaining: u8,
258}
259
260impl<'a, 'metadata> MethodsMetadataDecoder<'a, 'metadata> {
261 #[inline(always)]
262 fn new(
263 metadata: &'a mut &'metadata [u8],
264 container_kind: MethodsContainerKind,
265 num_methods: u8,
266 ) -> Self {
267 Self {
268 metadata,
269 container_kind,
270 remaining: num_methods,
271 }
272 }
273
274 #[inline(always)]
275 pub fn decode_next<'b>(&'b mut self) -> Option<MethodMetadataDecoder<'b, 'metadata>> {
276 if self.remaining == 0 {
277 return None;
278 }
279
280 self.remaining -= 1;
281
282 Some(MethodMetadataDecoder::new(
283 self.metadata,
284 self.container_kind,
285 ))
286 }
287}
288
289#[derive(Debug, Copy, Clone)]
290pub enum MethodKind {
291 Init,
293 UpdateStateless,
295 UpdateStatefulRo,
297 UpdateStatefulRw,
299 ViewStateless,
301 ViewStateful,
303}
304
305impl MethodKind {
306 #[inline(always)]
307 pub fn has_self(&self) -> bool {
308 match self {
309 MethodKind::Init | MethodKind::UpdateStateless | MethodKind::ViewStateless => false,
310 MethodKind::UpdateStatefulRo
311 | MethodKind::UpdateStatefulRw
312 | MethodKind::ViewStateful => true,
313 }
314 }
315}
316
317#[derive(Debug)]
318pub struct MethodMetadataItem<'metadata> {
319 pub method_name: &'metadata str,
320 pub method_kind: MethodKind,
321 pub num_arguments: u8,
322}
323
324#[derive(Debug)]
326pub struct MethodMetadataDecoder<'a, 'metadata> {
327 metadata: &'a mut &'metadata [u8],
328 container_kind: MethodsContainerKind,
329}
330
331impl<'a, 'metadata> MethodMetadataDecoder<'a, 'metadata> {
332 #[inline(always)]
333 pub fn new(metadata: &'a mut &'metadata [u8], container_kind: MethodsContainerKind) -> Self {
334 Self {
335 metadata,
336 container_kind,
337 }
338 }
339
340 #[inline(always)]
341 pub fn decode_next(
342 self,
343 ) -> Result<
344 (
345 ArgumentsMetadataDecoder<'a, 'metadata>,
346 MethodMetadataItem<'metadata>,
347 ),
348 MetadataDecodingError<'metadata>,
349 > {
350 if self.metadata.is_empty() {
351 return Err(MetadataDecodingError::NotEnoughMetadata);
352 }
353
354 let metadata_kind = ContractMetadataKind::try_from_u8(self.metadata[0]).ok_or(
356 MetadataDecodingError::InvalidFirstMetadataByte {
357 byte: self.metadata[0],
358 },
359 )?;
360 *self.metadata = &self.metadata[1..];
361
362 let method_kind = match metadata_kind {
363 ContractMetadataKind::Init => MethodKind::Init,
364 ContractMetadataKind::UpdateStateless => MethodKind::UpdateStateless,
365 ContractMetadataKind::UpdateStatefulRo => MethodKind::UpdateStatefulRo,
366 ContractMetadataKind::UpdateStatefulRw => MethodKind::UpdateStatefulRw,
367 ContractMetadataKind::ViewStateless => MethodKind::ViewStateless,
368 ContractMetadataKind::ViewStateful => MethodKind::ViewStateful,
369 ContractMetadataKind::Contract
371 | ContractMetadataKind::Trait
372 | ContractMetadataKind::EnvRo
373 | ContractMetadataKind::EnvRw
374 | ContractMetadataKind::TmpRo
375 | ContractMetadataKind::TmpRw
376 | ContractMetadataKind::SlotRo
377 | ContractMetadataKind::SlotRw
378 | ContractMetadataKind::Input
379 | ContractMetadataKind::Output => {
380 return Err(MetadataDecodingError::ExpectedMethodKind { metadata_kind });
381 }
382 };
383
384 let method_allowed = match self.container_kind {
385 MethodsContainerKind::Contract | MethodsContainerKind::Unknown => match method_kind {
386 MethodKind::Init
387 | MethodKind::UpdateStateless
388 | MethodKind::UpdateStatefulRo
389 | MethodKind::UpdateStatefulRw
390 | MethodKind::ViewStateless
391 | MethodKind::ViewStateful => true,
392 },
393 MethodsContainerKind::Trait => match method_kind {
394 MethodKind::Init
395 | MethodKind::UpdateStatefulRo
396 | MethodKind::UpdateStatefulRw
397 | MethodKind::ViewStateful => false,
398 MethodKind::UpdateStateless | MethodKind::ViewStateless => true,
399 },
400 };
401
402 if !method_allowed {
403 return Err(MetadataDecodingError::UnexpectedMethodKind {
404 method_kind,
405 container_kind: self.container_kind,
406 });
407 }
408
409 if self.metadata.is_empty() {
410 return Err(MetadataDecodingError::NotEnoughMetadata);
411 }
412
413 let method_name_length = usize::from(self.metadata[0]);
415 *self.metadata = &self.metadata[1..];
416
417 if self.metadata.len() < method_name_length + 1 {
419 return Err(MetadataDecodingError::NotEnoughMetadata);
420 }
421
422 let method_name = &self.metadata[..method_name_length];
423 *self.metadata = &self.metadata[method_name_length..];
424 let method_name = str::from_utf8(method_name)
425 .map_err(|error| MetadataDecodingError::InvalidMethodName { method_name, error })?;
426
427 let num_arguments = self.metadata[0];
429 *self.metadata = &self.metadata[1..];
430
431 let decoder = ArgumentsMetadataDecoder {
432 metadata: self.metadata,
433 method_kind,
434 remaining: num_arguments,
435 };
436 let item = MethodMetadataItem {
437 method_name,
438 method_kind,
439 num_arguments,
440 };
441
442 Ok((decoder, item))
443 }
444}
445
446#[derive(Debug, Copy, Clone)]
447pub enum ArgumentKind {
448 EnvRo,
450 EnvRw,
452 TmpRo,
454 TmpRw,
456 SlotRo,
458 SlotRw,
460 Input,
462 Output,
464}
465
466#[derive(Debug)]
467pub struct ArgumentMetadataItem<'metadata> {
468 pub argument_name: &'metadata str,
469 pub argument_kind: ArgumentKind,
470 pub type_details: Option<IoTypeDetails>,
475}
476
477#[derive(Debug)]
478pub struct ArgumentsMetadataDecoder<'a, 'metadata> {
479 metadata: &'a mut &'metadata [u8],
480 method_kind: MethodKind,
481 remaining: u8,
482}
483
484impl<'metadata> ArgumentsMetadataDecoder<'_, 'metadata> {
485 #[inline(always)]
486 pub fn decode_next<'a>(
487 &'a mut self,
488 ) -> Option<Result<ArgumentMetadataItem<'metadata>, MetadataDecodingError<'metadata>>> {
489 if self.remaining == 0 {
490 return None;
491 }
492
493 self.remaining -= 1;
494
495 Some(self.decode_argument())
496 }
497
498 #[inline(always)]
499 fn decode_argument<'a>(
500 &'a mut self,
501 ) -> Result<ArgumentMetadataItem<'metadata>, MetadataDecodingError<'metadata>> {
502 if self.metadata.is_empty() {
503 return Err(MetadataDecodingError::NotEnoughMetadata);
504 }
505
506 let metadata_kind = ContractMetadataKind::try_from_u8(self.metadata[0]).ok_or(
508 MetadataDecodingError::InvalidFirstMetadataByte {
509 byte: self.metadata[0],
510 },
511 )?;
512 *self.metadata = &self.metadata[1..];
513
514 let argument_kind = match metadata_kind {
515 ContractMetadataKind::EnvRo => ArgumentKind::EnvRo,
516 ContractMetadataKind::EnvRw => ArgumentKind::EnvRw,
517 ContractMetadataKind::TmpRo => ArgumentKind::TmpRo,
518 ContractMetadataKind::TmpRw => ArgumentKind::TmpRw,
519 ContractMetadataKind::SlotRo => ArgumentKind::SlotRo,
520 ContractMetadataKind::SlotRw => ArgumentKind::SlotRw,
521 ContractMetadataKind::Input => ArgumentKind::Input,
522 ContractMetadataKind::Output => ArgumentKind::Output,
523 ContractMetadataKind::Contract
525 | ContractMetadataKind::Trait
526 | ContractMetadataKind::Init
527 | ContractMetadataKind::UpdateStateless
528 | ContractMetadataKind::UpdateStatefulRo
529 | ContractMetadataKind::UpdateStatefulRw
530 | ContractMetadataKind::ViewStateless
531 | ContractMetadataKind::ViewStateful => {
532 return Err(MetadataDecodingError::ExpectedArgumentKind { metadata_kind });
533 }
534 };
535
536 let argument_allowed = match self.method_kind {
538 MethodKind::Init
539 | MethodKind::UpdateStateless
540 | MethodKind::UpdateStatefulRo
541 | MethodKind::UpdateStatefulRw => match argument_kind {
542 ArgumentKind::EnvRo
543 | ArgumentKind::EnvRw
544 | ArgumentKind::TmpRo
545 | ArgumentKind::TmpRw
546 | ArgumentKind::SlotRo
547 | ArgumentKind::SlotRw
548 | ArgumentKind::Input
549 | ArgumentKind::Output => true,
550 },
551 MethodKind::ViewStateless | MethodKind::ViewStateful => match argument_kind {
552 ArgumentKind::EnvRo
553 | ArgumentKind::SlotRo
554 | ArgumentKind::Input
555 | ArgumentKind::Output => true,
556 ArgumentKind::EnvRw
557 | ArgumentKind::TmpRo
558 | ArgumentKind::TmpRw
559 | ArgumentKind::SlotRw => false,
560 },
561 };
562
563 if !argument_allowed {
564 return Err(MetadataDecodingError::UnexpectedArgumentKind {
565 argument_kind,
566 method_kind: self.method_kind,
567 });
568 }
569
570 let (argument_name, type_details) = match argument_kind {
571 ArgumentKind::EnvRo | ArgumentKind::EnvRw => ("env", None),
572 ArgumentKind::TmpRo
573 | ArgumentKind::TmpRw
574 | ArgumentKind::SlotRo
575 | ArgumentKind::SlotRw
576 | ArgumentKind::Input
577 | ArgumentKind::Output => {
578 if self.metadata.is_empty() {
579 return Err(MetadataDecodingError::NotEnoughMetadata);
580 }
581
582 let argument_name_length = usize::from(self.metadata[0]);
584 *self.metadata = &self.metadata[1..];
585
586 if self.metadata.len() < argument_name_length {
588 return Err(MetadataDecodingError::NotEnoughMetadata);
589 }
590
591 let argument_name = &self.metadata[..argument_name_length];
592 *self.metadata = &self.metadata[argument_name_length..];
593 let argument_name = str::from_utf8(argument_name).map_err(|error| {
594 MetadataDecodingError::InvalidArgumentName {
595 argument_name,
596 error,
597 }
598 })?;
599
600 let recommended_capacity = match argument_kind {
601 ArgumentKind::EnvRo
602 | ArgumentKind::EnvRw
603 | ArgumentKind::TmpRo
604 | ArgumentKind::TmpRw
605 | ArgumentKind::SlotRo
606 | ArgumentKind::SlotRw => None,
607 ArgumentKind::Input => {
608 let recommended_capacity;
609 (recommended_capacity, *self.metadata) =
610 IoTypeMetadataKind::type_details(self.metadata).ok_or(
611 MetadataDecodingError::InvalidArgumentIoType {
612 argument_name,
613 argument_kind,
614 },
615 )?;
616
617 Some(recommended_capacity)
618 }
619 ArgumentKind::Output => {
620 let last_argument = self.remaining == 0;
621 if matches!((self.method_kind, last_argument), (MethodKind::Init, true)) {
623 None
624 } else {
625 let recommended_capacity;
626 (recommended_capacity, *self.metadata) =
627 IoTypeMetadataKind::type_details(self.metadata).ok_or(
628 MetadataDecodingError::InvalidArgumentIoType {
629 argument_name,
630 argument_kind,
631 },
632 )?;
633
634 Some(recommended_capacity)
635 }
636 }
637 };
638
639 (argument_name, recommended_capacity)
640 }
641 };
642
643 Ok(ArgumentMetadataItem {
644 argument_name,
645 argument_kind,
646 type_details,
647 })
648 }
649}