1use std::fmt::{Display, Formatter};
2
3use fuel_abi_types::{
4 abi::full_program::FullTypeApplication,
5 utils::{self, extract_array_len, extract_generic_name, extract_str_len, has_tuple_format},
6};
7use proc_macro2::{Ident, TokenStream};
8use quote::{ToTokens, quote};
9
10use crate::{
11 error::{Result, error},
12 program_bindings::utils::sdk_provided_custom_types_lookup,
13 utils::TypePath,
14};
15
16#[derive(Debug, Clone, PartialEq)]
17pub enum GenericType {
18 Named(Ident),
19 Constant(usize),
20}
21
22impl ToTokens for GenericType {
23 fn to_tokens(&self, tokens: &mut TokenStream) {
24 let stream = match self {
25 GenericType::Named(ident) => ident.to_token_stream(),
26 GenericType::Constant(constant) => constant.to_token_stream(),
27 };
28
29 tokens.extend(stream);
30 }
31}
32
33#[derive(Debug, Clone)]
37pub enum ResolvedType {
38 Unit,
39 Primitive(TypePath),
40 StructOrEnum {
41 path: TypePath,
42 generics: Vec<ResolvedType>,
43 },
44 Array(Box<ResolvedType>, usize),
45 Tuple(Vec<ResolvedType>),
46 Generic(GenericType),
47}
48
49impl ResolvedType {
50 pub fn generics(&self) -> Vec<GenericType> {
51 match self {
52 ResolvedType::StructOrEnum {
53 generics: elements, ..
54 }
55 | ResolvedType::Tuple(elements) => {
56 elements.iter().flat_map(|el| el.generics()).collect()
57 }
58 ResolvedType::Array(el, _) => el.generics(),
59 ResolvedType::Generic(inner) => vec![inner.clone()],
60 _ => vec![],
61 }
62 }
63}
64
65impl ToTokens for ResolvedType {
66 fn to_tokens(&self, tokens: &mut TokenStream) {
67 let tokenized = match self {
68 ResolvedType::Unit => quote! {()},
69 ResolvedType::Primitive(path) => path.into_token_stream(),
70 ResolvedType::StructOrEnum { path, generics } => {
71 if generics.is_empty() {
72 path.to_token_stream()
73 } else {
74 quote! { #path<#(#generics),*>}
75 }
76 }
77 ResolvedType::Array(el, count) => quote! { [#el; #count]},
78 ResolvedType::Tuple(elements) => {
79 quote! { (#(#elements,)*) }
83 }
84 ResolvedType::Generic(generic_type) => generic_type.into_token_stream(),
85 };
86
87 tokens.extend(tokenized)
88 }
89}
90
91impl Display for ResolvedType {
92 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
93 write!(f, "{}", self.to_token_stream())
94 }
95}
96
97pub(crate) struct TypeResolver {
99 current_mod: TypePath,
101}
102
103impl Default for TypeResolver {
104 fn default() -> Self {
105 TypeResolver::new(Default::default())
106 }
107}
108
109impl TypeResolver {
110 pub(crate) fn new(current_mod: TypePath) -> Self {
111 Self { current_mod }
112 }
113
114 pub(crate) fn resolve(&self, type_application: &FullTypeApplication) -> Result<ResolvedType> {
115 let resolvers = [
116 Self::try_as_primitive_type,
117 Self::try_as_bits256,
118 Self::try_as_generic,
119 Self::try_as_array,
120 Self::try_as_sized_ascii_string,
121 Self::try_as_ascii_string,
122 Self::try_as_tuple,
123 Self::try_as_raw_slice,
124 Self::try_as_custom_type,
125 ];
126
127 for resolver in resolvers {
128 if let Some(resolved) = resolver(self, type_application)? {
129 return Ok(resolved);
130 }
131 }
132
133 let type_field = &type_application.type_decl.type_field;
134 Err(error!("could not resolve '{type_field}' to any known type"))
135 }
136
137 fn resolve_multiple(
138 &self,
139 type_applications: &[FullTypeApplication],
140 ) -> Result<Vec<ResolvedType>> {
141 type_applications
142 .iter()
143 .map(|type_application| self.resolve(type_application))
144 .collect()
145 }
146
147 fn try_as_generic(
148 &self,
149 type_application: &FullTypeApplication,
150 ) -> Result<Option<ResolvedType>> {
151 let Some(name) = extract_generic_name(&type_application.type_decl.type_field) else {
152 return Ok(None);
153 };
154
155 let ident = utils::safe_ident(&name);
156 Ok(Some(ResolvedType::Generic(GenericType::Named(ident))))
157 }
158
159 fn try_as_array(&self, type_application: &FullTypeApplication) -> Result<Option<ResolvedType>> {
160 let type_decl = &type_application.type_decl;
161 let Some(len) = extract_array_len(&type_decl.type_field) else {
162 return Ok(None);
163 };
164
165 let components = self.resolve_multiple(&type_decl.components)?;
166 let type_inside = match components.as_slice() {
167 [single_type] => single_type,
168 other => {
169 return Err(error!(
170 "array must have only one component. Actual components: {other:?}"
171 ));
172 }
173 };
174
175 Ok(Some(ResolvedType::Array(
176 Box::new(type_inside.clone()),
177 len,
178 )))
179 }
180
181 fn try_as_sized_ascii_string(
182 &self,
183 type_application: &FullTypeApplication,
184 ) -> Result<Option<ResolvedType>> {
185 let Some(len) = extract_str_len(&type_application.type_decl.type_field) else {
186 return Ok(None);
187 };
188
189 let path =
190 TypePath::new("::fuels::types::SizedAsciiString").expect("this is a valid TypePath");
191 Ok(Some(ResolvedType::StructOrEnum {
192 path,
193 generics: vec![ResolvedType::Generic(GenericType::Constant(len))],
194 }))
195 }
196
197 fn try_as_ascii_string(
198 &self,
199 type_application: &FullTypeApplication,
200 ) -> Result<Option<ResolvedType>> {
201 let maybe_resolved = (type_application.type_decl.type_field == "str").then(|| {
202 let path =
203 TypePath::new("::fuels::types::AsciiString").expect("this is a valid TypePath");
204 ResolvedType::StructOrEnum {
205 path,
206 generics: vec![],
207 }
208 });
209
210 Ok(maybe_resolved)
211 }
212
213 fn try_as_tuple(&self, type_application: &FullTypeApplication) -> Result<Option<ResolvedType>> {
214 let type_decl = &type_application.type_decl;
215 if !has_tuple_format(&type_decl.type_field) {
216 return Ok(None);
217 }
218 let inner_types = self.resolve_multiple(&type_decl.components)?;
219
220 Ok(Some(ResolvedType::Tuple(inner_types)))
221 }
222
223 fn try_as_primitive_type(
224 &self,
225 type_decl: &FullTypeApplication,
226 ) -> Result<Option<ResolvedType>> {
227 let type_field = &type_decl.type_decl.type_field;
228
229 let maybe_resolved = match type_field.as_str() {
230 "()" => Some(ResolvedType::Unit),
231 "bool" | "u8" | "u16" | "u32" | "u64" => {
232 let path = format!("::core::primitive::{type_field}");
233 let type_path = TypePath::new(path).expect("to be a valid path");
234
235 Some(ResolvedType::Primitive(type_path))
236 }
237 "struct std::u128::U128" | "struct U128" => {
238 let u128_path = TypePath::new("::core::primitive::u128").expect("is correct");
239 Some(ResolvedType::Primitive(u128_path))
240 }
241 "u256" => {
242 let u256_path = TypePath::new("::fuels::types::U256").expect("is correct");
243 Some(ResolvedType::Primitive(u256_path))
244 }
245 _ => None,
246 };
247
248 Ok(maybe_resolved)
249 }
250
251 fn try_as_bits256(
252 &self,
253 type_application: &FullTypeApplication,
254 ) -> Result<Option<ResolvedType>> {
255 if type_application.type_decl.type_field != "b256" {
256 return Ok(None);
257 }
258
259 let path = TypePath::new("::fuels::types::Bits256").expect("to be valid");
260 Ok(Some(ResolvedType::StructOrEnum {
261 path,
262 generics: vec![],
263 }))
264 }
265
266 fn try_as_raw_slice(
267 &self,
268 type_application: &FullTypeApplication,
269 ) -> Result<Option<ResolvedType>> {
270 if type_application.type_decl.type_field != "raw untyped slice" {
271 return Ok(None);
272 }
273
274 let path = TypePath::new("::fuels::types::RawSlice").expect("this is a valid TypePath");
275 Ok(Some(ResolvedType::StructOrEnum {
276 path,
277 generics: vec![],
278 }))
279 }
280
281 fn try_as_custom_type(
282 &self,
283 type_application: &FullTypeApplication,
284 ) -> Result<Option<ResolvedType>> {
285 let type_decl = &type_application.type_decl;
286
287 if !type_decl.is_custom_type() {
288 return Ok(None);
289 }
290
291 let original_path = type_decl.custom_type_path()?;
292
293 let used_path = sdk_provided_custom_types_lookup()
294 .get(&original_path)
295 .cloned()
296 .unwrap_or_else(|| original_path.relative_path_from(&self.current_mod));
297
298 let generics = self.resolve_multiple(&type_application.type_arguments)?;
299
300 Ok(Some(ResolvedType::StructOrEnum {
301 path: used_path,
302 generics,
303 }))
304 }
305}
306
307#[cfg(test)]
308mod tests {
309 use std::{collections::HashMap, str::FromStr};
310
311 use fuel_abi_types::{
312 abi::{
313 full_program::FullTypeDeclaration,
314 unified_program::{UnifiedTypeApplication, UnifiedTypeDeclaration},
315 },
316 utils::ident,
317 };
318
319 use super::*;
320
321 #[test]
322 fn correctly_extracts_used_generics() {
323 let resolved_type = ResolvedType::StructOrEnum {
324 path: Default::default(),
325 generics: vec![
326 ResolvedType::Tuple(vec![ResolvedType::Array(
327 Box::new(ResolvedType::StructOrEnum {
328 path: Default::default(),
329 generics: vec![
330 ResolvedType::Generic(GenericType::Named(ident("A"))),
331 ResolvedType::Generic(GenericType::Constant(10)),
332 ],
333 }),
334 2,
335 )]),
336 ResolvedType::Generic(GenericType::Named(ident("B"))),
337 ],
338 };
339
340 let generics = resolved_type.generics();
341
342 assert_eq!(
343 generics,
344 vec![
345 GenericType::Named(ident("A")),
346 GenericType::Constant(10),
347 GenericType::Named(ident("B"))
348 ]
349 )
350 }
351
352 fn test_resolve_first_type(
353 expected: &str,
354 type_declarations: &[UnifiedTypeDeclaration],
355 ) -> Result<()> {
356 let types = type_declarations
357 .iter()
358 .map(|td| (td.type_id, td.clone()))
359 .collect::<HashMap<_, _>>();
360 let type_application = UnifiedTypeApplication {
361 type_id: type_declarations[0].type_id,
362 ..Default::default()
363 };
364
365 let application = FullTypeApplication::from_counterpart(&type_application, &types);
366 let resolved_type = TypeResolver::default()
367 .resolve(&application)
368 .map_err(|e| e.combine(error!("failed to resolve {:?}", type_application)))?;
369 let actual = resolved_type.to_token_stream().to_string();
370
371 let expected = TokenStream::from_str(expected).unwrap().to_string();
372 assert_eq!(actual, expected);
373
374 Ok(())
375 }
376
377 fn test_resolve_primitive_type(type_field: &str, expected: &str) -> Result<()> {
378 test_resolve_first_type(
379 expected,
380 &[UnifiedTypeDeclaration {
381 type_id: 0,
382 type_field: type_field.to_string(),
383 ..Default::default()
384 }],
385 )
386 }
387
388 #[test]
389 fn test_resolve_u8() -> Result<()> {
390 test_resolve_primitive_type("u8", "::core::primitive::u8")
391 }
392
393 #[test]
394 fn test_resolve_u16() -> Result<()> {
395 test_resolve_primitive_type("u16", "::core::primitive::u16")
396 }
397
398 #[test]
399 fn test_resolve_u32() -> Result<()> {
400 test_resolve_primitive_type("u32", "::core::primitive::u32")
401 }
402
403 #[test]
404 fn test_resolve_u64() -> Result<()> {
405 test_resolve_primitive_type("u64", "::core::primitive::u64")
406 }
407
408 #[test]
409 fn test_resolve_bool() -> Result<()> {
410 test_resolve_primitive_type("bool", "::core::primitive::bool")
411 }
412
413 #[test]
414 fn test_resolve_b256() -> Result<()> {
415 test_resolve_primitive_type("b256", "::fuels::types::Bits256")
416 }
417
418 #[test]
419 fn test_resolve_unit() -> Result<()> {
420 test_resolve_primitive_type("()", "()")
421 }
422
423 #[test]
424 fn test_resolve_array() -> Result<()> {
425 test_resolve_first_type(
426 "[::core::primitive::u8 ; 3usize]",
427 &[
428 UnifiedTypeDeclaration {
429 type_id: 0,
430 type_field: "[u8; 3]".to_string(),
431 components: Some(vec![UnifiedTypeApplication {
432 type_id: 1,
433 ..Default::default()
434 }]),
435 ..Default::default()
436 },
437 UnifiedTypeDeclaration {
438 type_id: 1,
439 type_field: "u8".to_string(),
440 ..Default::default()
441 },
442 ],
443 )
444 }
445
446 #[test]
447 fn test_resolve_vector() -> Result<()> {
448 test_resolve_first_type(
449 ":: std :: vec :: Vec",
450 &[
451 UnifiedTypeDeclaration {
452 type_id: 0,
453 type_field: "struct std::vec::Vec".to_string(),
454 components: Some(vec![
455 UnifiedTypeApplication {
456 name: "buf".to_string(),
457 type_id: 2,
458 type_arguments: Some(vec![UnifiedTypeApplication {
459 type_id: 1,
460 ..Default::default()
461 }]),
462 error_message: None,
463 },
464 UnifiedTypeApplication {
465 name: "len".to_string(),
466 type_id: 3,
467 ..Default::default()
468 },
469 ]),
470 type_parameters: Some(vec![1]),
471 alias_of: None,
472 },
473 UnifiedTypeDeclaration {
474 type_id: 1,
475 type_field: "generic T".to_string(),
476 ..Default::default()
477 },
478 UnifiedTypeDeclaration {
479 type_id: 2,
480 type_field: "raw untyped ptr".to_string(),
481 ..Default::default()
482 },
483 UnifiedTypeDeclaration {
484 type_id: 3,
485 type_field: "struct std::vec::RawVec".to_string(),
486 components: Some(vec![
487 UnifiedTypeApplication {
488 name: "ptr".to_string(),
489 type_id: 2,
490 ..Default::default()
491 },
492 UnifiedTypeApplication {
493 name: "cap".to_string(),
494 type_id: 4,
495 ..Default::default()
496 },
497 ]),
498 type_parameters: Some(vec![1]),
499 alias_of: None,
500 },
501 UnifiedTypeDeclaration {
502 type_id: 4,
503 type_field: "u64".to_string(),
504 ..Default::default()
505 },
506 UnifiedTypeDeclaration {
507 type_id: 5,
508 type_field: "u8".to_string(),
509 ..Default::default()
510 },
511 ],
512 )
513 }
514
515 #[test]
516 fn test_resolve_bytes() -> Result<()> {
517 test_resolve_first_type(
518 ":: fuels :: types :: Bytes",
519 &[
520 UnifiedTypeDeclaration {
521 type_id: 0,
522 type_field: "struct String".to_string(),
523 components: Some(vec![UnifiedTypeApplication {
524 name: "bytes".to_string(),
525 type_id: 1,
526 ..Default::default()
527 }]),
528 ..Default::default()
529 },
530 UnifiedTypeDeclaration {
531 type_id: 0,
532 type_field: "struct std::bytes::Bytes".to_string(),
533 components: Some(vec![
534 UnifiedTypeApplication {
535 name: "buf".to_string(),
536 type_id: 1,
537 ..Default::default()
538 },
539 UnifiedTypeApplication {
540 name: "len".to_string(),
541 type_id: 3,
542 ..Default::default()
543 },
544 ]),
545 ..Default::default()
546 },
547 UnifiedTypeDeclaration {
548 type_id: 1,
549 type_field: "struct std::bytes::RawBytes".to_string(),
550 components: Some(vec![
551 UnifiedTypeApplication {
552 name: "ptr".to_string(),
553 type_id: 2,
554 ..Default::default()
555 },
556 UnifiedTypeApplication {
557 name: "cap".to_string(),
558 type_id: 3,
559 ..Default::default()
560 },
561 ]),
562 ..Default::default()
563 },
564 UnifiedTypeDeclaration {
565 type_id: 2,
566 type_field: "raw untyped ptr".to_string(),
567 ..Default::default()
568 },
569 UnifiedTypeDeclaration {
570 type_id: 3,
571 type_field: "u64".to_string(),
572 ..Default::default()
573 },
574 ],
575 )
576 }
577
578 #[test]
579 fn test_resolve_std_string() -> Result<()> {
580 test_resolve_first_type(
581 ":: std :: string :: String",
582 &[
583 UnifiedTypeDeclaration {
584 type_id: 0,
585 type_field: "struct std::string::String".to_string(),
586 components: Some(vec![UnifiedTypeApplication {
587 name: "bytes".to_string(),
588 type_id: 1,
589 ..Default::default()
590 }]),
591 ..Default::default()
592 },
593 UnifiedTypeDeclaration {
594 type_id: 1,
595 type_field: "struct std::bytes::Bytes".to_string(),
596 components: Some(vec![
597 UnifiedTypeApplication {
598 name: "buf".to_string(),
599 type_id: 2,
600 ..Default::default()
601 },
602 UnifiedTypeApplication {
603 name: "len".to_string(),
604 type_id: 4,
605 ..Default::default()
606 },
607 ]),
608 ..Default::default()
609 },
610 UnifiedTypeDeclaration {
611 type_id: 2,
612 type_field: "struct std::bytes::RawBytes".to_string(),
613 components: Some(vec![
614 UnifiedTypeApplication {
615 name: "ptr".to_string(),
616 type_id: 3,
617 ..Default::default()
618 },
619 UnifiedTypeApplication {
620 name: "cap".to_string(),
621 type_id: 4,
622 ..Default::default()
623 },
624 ]),
625 ..Default::default()
626 },
627 UnifiedTypeDeclaration {
628 type_id: 3,
629 type_field: "raw untyped ptr".to_string(),
630 ..Default::default()
631 },
632 UnifiedTypeDeclaration {
633 type_id: 4,
634 type_field: "u64".to_string(),
635 ..Default::default()
636 },
637 ],
638 )
639 }
640
641 #[test]
642 fn test_resolve_static_str() -> Result<()> {
643 test_resolve_primitive_type("str[3]", ":: fuels :: types :: SizedAsciiString < 3usize >")
644 }
645
646 #[test]
647 fn test_resolve_struct() -> Result<()> {
648 test_resolve_first_type(
649 "self :: SomeStruct",
650 &[
651 UnifiedTypeDeclaration {
652 type_id: 0,
653 type_field: "struct SomeStruct".to_string(),
654 components: Some(vec![
655 UnifiedTypeApplication {
656 name: "foo".to_string(),
657 type_id: 1,
658 ..Default::default()
659 },
660 UnifiedTypeApplication {
661 name: "bar".to_string(),
662 type_id: 2,
663 ..Default::default()
664 },
665 ]),
666 type_parameters: Some(vec![1]),
667 alias_of: None,
668 },
669 UnifiedTypeDeclaration {
670 type_id: 1,
671 type_field: "generic T".to_string(),
672 ..Default::default()
673 },
674 UnifiedTypeDeclaration {
675 type_id: 2,
676 type_field: "u8".to_string(),
677 ..Default::default()
678 },
679 ],
680 )
681 }
682
683 #[test]
684 fn test_resolve_enum() -> Result<()> {
685 test_resolve_first_type(
686 "self :: SomeEnum",
687 &[
688 UnifiedTypeDeclaration {
689 type_id: 0,
690 type_field: "enum SomeEnum".to_string(),
691 components: Some(vec![
692 UnifiedTypeApplication {
693 name: "foo".to_string(),
694 type_id: 1,
695 ..Default::default()
696 },
697 UnifiedTypeApplication {
698 name: "bar".to_string(),
699 type_id: 2,
700 ..Default::default()
701 },
702 ]),
703 type_parameters: Some(vec![1]),
704 alias_of: None,
705 },
706 UnifiedTypeDeclaration {
707 type_id: 1,
708 type_field: "generic T".to_string(),
709 ..Default::default()
710 },
711 UnifiedTypeDeclaration {
712 type_id: 2,
713 type_field: "u8".to_string(),
714 ..Default::default()
715 },
716 ],
717 )
718 }
719
720 #[test]
721 fn test_resolve_tuple() -> Result<()> {
722 test_resolve_first_type(
723 "(::core::primitive::u8, ::core::primitive::u16, ::core::primitive::bool, T,)",
724 &[
725 UnifiedTypeDeclaration {
726 type_id: 0,
727 type_field: "(u8, u16, bool, T)".to_string(),
728 components: Some(vec![
729 UnifiedTypeApplication {
730 type_id: 1,
731 ..Default::default()
732 },
733 UnifiedTypeApplication {
734 type_id: 2,
735 ..Default::default()
736 },
737 UnifiedTypeApplication {
738 type_id: 3,
739 ..Default::default()
740 },
741 UnifiedTypeApplication {
742 type_id: 4,
743 ..Default::default()
744 },
745 ]),
746 type_parameters: Some(vec![4]),
747 alias_of: None,
748 },
749 UnifiedTypeDeclaration {
750 type_id: 1,
751 type_field: "u8".to_string(),
752 ..Default::default()
753 },
754 UnifiedTypeDeclaration {
755 type_id: 2,
756 type_field: "u16".to_string(),
757 ..Default::default()
758 },
759 UnifiedTypeDeclaration {
760 type_id: 3,
761 type_field: "bool".to_string(),
762 ..Default::default()
763 },
764 UnifiedTypeDeclaration {
765 type_id: 4,
766 type_field: "generic T".to_string(),
767 ..Default::default()
768 },
769 ],
770 )
771 }
772
773 #[test]
774 fn custom_types_uses_correct_path_for_sdk_provided_types() {
775 let resolver = TypeResolver::default();
776 for (type_path, expected_path) in sdk_provided_custom_types_lookup() {
777 let type_application = given_fn_arg_of_custom_type(&type_path);
779
780 let resolved_type = resolver.resolve(&type_application).unwrap();
782
783 let expected_type_name = expected_path.into_token_stream();
785 assert_eq!(
786 resolved_type.to_token_stream().to_string(),
787 expected_type_name.to_string()
788 );
789 }
790 }
791
792 fn given_fn_arg_of_custom_type(type_path: &TypePath) -> FullTypeApplication {
793 FullTypeApplication {
794 name: "some_arg".to_string(),
795 type_decl: FullTypeDeclaration {
796 type_field: format!("struct {type_path}"),
797 components: vec![],
798 type_parameters: vec![],
799 alias_of: None,
800 },
801 type_arguments: vec![],
802 error_message: None,
803 }
804 }
805}