Skip to content

Commit c0a04b9

Browse files
committed
Add support for ABI alias types
1 parent aed2d97 commit c0a04b9

File tree

9 files changed

+153
-8
lines changed

9 files changed

+153
-8
lines changed

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ resolver = "2"
88
members = [
99
"e2e",
1010
"examples/codec",
11+
"examples/codegen",
1112
"examples/contracts",
1213
"examples/cookbook",
1314
"examples/debugging",
@@ -52,7 +53,8 @@ cynic = { version = "3.1.0", default-features = false }
5253
test-case = { version = "3.3", default-features = false }
5354
eth-keystore = "0.5.0"
5455
flate2 = { version = "1.0", default-features = false }
55-
fuel-abi-types = "0.13.0"
56+
#fuel-abi-types = "0.13.0"
57+
fuel-abi-types = { path = "../fuel-abi-types", version = "0.13.0" }
5658
futures = "0.3.29"
5759
hex = { version = "0.4.3", default-features = false }
5860
itertools = "0.12.0"

examples/codegen/Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "fuels-example-codegen"
3+
version = { workspace = true }
4+
authors = { workspace = true }
5+
edition = { workspace = true }
6+
homepage = { workspace = true }
7+
license = { workspace = true }
8+
publish = false
9+
repository = { workspace = true }
10+
description = "Fuel Rust SDK macro examples."
11+
12+
[dev-dependencies]
13+
fuels = { workspace = true, features = ["default"] }
14+
tokio = { workspace = true, features = ["full"] }

examples/codegen/src/lib.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
extern crate alloc;
2+
3+
#[cfg(test)]
4+
mod tests {
5+
#[test]
6+
fn example_alias() {
7+
use fuels::code_gen::*;
8+
9+
let target = AbigenTarget::new(
10+
"MyContract".into(),
11+
Abi::load_from("/home/joao/dev/sway/_test_aliases_abi/out/debug/test-case-abi.json")
12+
.unwrap(),
13+
ProgramType::Contract,
14+
);
15+
let targets = vec![target];
16+
17+
let abigen = Abigen::generate(targets, false).expect("abigen generation failed");
18+
}
19+
}

packages/fuels-code-gen/src/program_bindings/abigen.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,15 @@ impl Abigen {
3434
/// for, and of what nature (Contract, Script or Predicate).
3535
/// * `no_std`: don't use the Rust std library.
3636
pub fn generate(targets: Vec<AbigenTarget>, no_std: bool) -> Result<TokenStream> {
37+
// eprintln!("{:#?}", targets);
38+
3739
let generated_code = Self::generate_code(no_std, targets)?;
3840

41+
eprintln!(
42+
"========================== CODE: \n {:#}",
43+
generated_code.code()
44+
);
45+
3946
let use_statements = generated_code.use_statements_for_uniquely_named_types();
4047

4148
let code = if no_std {
@@ -67,10 +74,18 @@ impl Abigen {
6774

6875
fn generate_code(no_std: bool, parsed_targets: Vec<AbigenTarget>) -> Result<GeneratedCode> {
6976
let custom_types = Self::filter_custom_types(&parsed_targets);
70-
let shared_types = Self::filter_shared_types(custom_types);
77+
let mut shared_types = Self::filter_shared_types(custom_types);
78+
79+
let alias_types = Self::filter_alias_types(&parsed_targets)
80+
.cloned()
81+
.collect::<Vec<_>>();
82+
shared_types.clear();
83+
shared_types.extend(alias_types);
7184

7285
let bindings = Self::generate_all_bindings(parsed_targets, no_std, &shared_types)?;
86+
eprintln!("bindings {:#}", bindings.code().to_string());
7387
let shared_types = Self::generate_shared_types(shared_types, no_std)?;
88+
eprintln!("shared_types {:#}", shared_types.code().to_string());
7489

7590
let mod_name = ident("abigen_bindings");
7691
Ok(shared_types.merge(bindings).wrap_in_mod(mod_name))
@@ -94,11 +109,14 @@ impl Abigen {
94109
no_std: bool,
95110
shared_types: &HashSet<FullTypeDeclaration>,
96111
) -> Result<GeneratedCode> {
112+
eprintln!("generate_bindings shared_types {:#?}", shared_types);
97113
let mod_name = ident(&format!("{}_mod", &target.name.to_snake_case()));
98114

99115
let recompile_trigger =
100116
Self::generate_macro_recompile_trigger(target.source.path.as_ref(), no_std);
101117
let types = generate_types(&target.source.abi.types, shared_types, no_std)?;
118+
eprintln!("generate_bindings types {:#?}", types);
119+
102120
let bindings = generate_bindings(target, no_std)?;
103121
Ok(recompile_trigger
104122
.merge(types)
@@ -145,6 +163,15 @@ impl Abigen {
145163
.filter(|ttype| ttype.is_custom_type())
146164
}
147165

166+
fn filter_alias_types(
167+
all_types: &[AbigenTarget],
168+
) -> impl Iterator<Item = &FullTypeDeclaration> {
169+
all_types
170+
.iter()
171+
.flat_map(|target| &target.source.abi.types)
172+
.filter(|ttype| ttype.is_alias_type())
173+
}
174+
148175
/// A type is considered "shared" if it appears at least twice in
149176
/// `all_custom_types`.
150177
///

packages/fuels-code-gen/src/program_bindings/abigen/abigen_target.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ use crate::error::{Error, Result, error};
1212

1313
#[derive(Debug, Clone)]
1414
pub struct AbigenTarget {
15-
pub(crate) name: String,
16-
pub(crate) source: Abi,
17-
pub(crate) program_type: ProgramType,
15+
pub name: String,
16+
pub source: Abi,
17+
pub program_type: ProgramType,
1818
}
1919

2020
impl AbigenTarget {

packages/fuels-code-gen/src/program_bindings/custom_types.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ pub(crate) fn generate_types<'a, T: IntoIterator<Item = &'a FullTypeDeclaration>
4040
.map(|ttype: &FullTypeDeclaration| {
4141
if shared_types.contains(ttype) {
4242
reexport_the_shared_type(ttype, no_std)
43+
} else if ttype.is_alias_type() {
44+
expand_alias_type(ttype, no_std)
4345
} else if ttype.is_struct_type() {
4446
expand_custom_struct(ttype, no_std)
4547
} else {
@@ -51,13 +53,36 @@ pub(crate) fn generate_types<'a, T: IntoIterator<Item = &'a FullTypeDeclaration>
5153
})
5254
}
5355

56+
fn expand_alias_type(ttype: &FullTypeDeclaration, no_std: bool) -> Result<GeneratedCode> {
57+
let type_path = ttype.alias_type_path().expect("This must be an alias type");
58+
59+
let type_mod = type_path.parent();
60+
61+
let top_lvl_mod = TypePath::default();
62+
let from_current_mod_to_top_level = top_lvl_mod.relative_path_from(&type_mod);
63+
64+
let path = from_current_mod_to_top_level
65+
.append(type_path);
66+
67+
let the_reexport = quote! {pub use #path;};
68+
69+
Ok(GeneratedCode::new(the_reexport, Default::default(), no_std).wrap_in_mod(type_mod))
70+
}
71+
5472
/// Instead of generating bindings for `ttype` this fn will just generate a `pub use` pointing to
5573
/// the already generated equivalent shared type.
5674
fn reexport_the_shared_type(ttype: &FullTypeDeclaration, no_std: bool) -> Result<GeneratedCode> {
75+
5776
// e.g. some_library::another_mod::SomeStruct
58-
let type_path = ttype
77+
let type_path = if ttype.is_custom_type() {
78+
ttype
5979
.custom_type_path()
60-
.expect("This must be a custom type due to the previous filter step");
80+
.expect("This must be a custom type due to the previous filter step")
81+
} else if ttype.is_alias_type() {
82+
ttype.alias_type_path().expect("This must be an alias type")
83+
} else {
84+
unreachable!()
85+
};
6186

6287
let type_mod = type_path.parent();
6388

@@ -85,6 +110,10 @@ fn reexport_the_shared_type(ttype: &FullTypeDeclaration, no_std: bool) -> Result
85110
// implementation details of the contract's Vec type and are not directly
86111
// used in the SDK.
87112
fn should_skip_codegen(type_decl: &FullTypeDeclaration) -> bool {
113+
if type_decl.is_alias_type() {
114+
return false;
115+
}
116+
88117
if !type_decl.is_custom_type() {
89118
return true;
90119
}

packages/fuels-code-gen/src/program_bindings/resolved_type.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use std::fmt::{Display, Formatter};
1+
use std::{
2+
fmt::{Display, Formatter},
3+
sync::Arc,
4+
};
25

36
use fuel_abi_types::{
47
abi::full_program::FullTypeApplication,
@@ -44,6 +47,10 @@ pub enum ResolvedType {
4447
Array(Box<ResolvedType>, usize),
4548
Tuple(Vec<ResolvedType>),
4649
Generic(GenericType),
50+
Alias {
51+
path: TypePath,
52+
target: Arc<ResolvedType>,
53+
},
4754
}
4855

4956
impl ResolvedType {
@@ -57,6 +64,7 @@ impl ResolvedType {
5764
}
5865
ResolvedType::Array(el, _) => el.generics(),
5966
ResolvedType::Generic(inner) => vec![inner.clone()],
67+
ResolvedType::Alias { target, .. } => target.generics(),
6068
_ => vec![],
6169
}
6270
}
@@ -82,6 +90,10 @@ impl ToTokens for ResolvedType {
8290
quote! { (#(#elements,)*) }
8391
}
8492
ResolvedType::Generic(generic_type) => generic_type.into_token_stream(),
93+
ResolvedType::Alias { path, .. } => {
94+
path.to_token_stream()
95+
// target.as_ref().into_token_stream()
96+
}
8597
};
8698

8799
tokens.extend(tokenized)
@@ -122,6 +134,7 @@ impl TypeResolver {
122134
Self::try_as_tuple,
123135
Self::try_as_raw_slice,
124136
Self::try_as_custom_type,
137+
Self::try_as_alias_type,
125138
];
126139

127140
for resolver in resolvers {
@@ -302,6 +315,42 @@ impl TypeResolver {
302315
generics,
303316
}))
304317
}
318+
319+
fn try_as_alias_type(
320+
&self,
321+
type_application: &FullTypeApplication,
322+
) -> Result<Option<ResolvedType>> {
323+
let type_decl = &type_application.type_decl;
324+
325+
if !type_decl.is_alias_type() {
326+
return Ok(None);
327+
}
328+
329+
let Some(alias_of) = &type_decl.alias_of else {
330+
return Ok(None);
331+
};
332+
333+
let target = Arc::new(self.resolve(alias_of)?);
334+
335+
let path = if let Ok(custom_type_path) = type_decl.custom_type_path() {
336+
custom_type_path
337+
} else {
338+
type_decl.alias_type_path()?
339+
};
340+
341+
let path = path.relative_path_from(&self.current_mod);
342+
343+
Ok(Some(ResolvedType::Alias { path, target }))
344+
345+
// if let Some(alias_app) = &type_decl.alias_of {
346+
// let resolver = TypeResolver::default();
347+
// let resolved_alias = resolver.resolve(alias_app)?;
348+
// let resolved_type = ResolvedType::Alias(Arc::new(resolved_alias));
349+
// return Ok(Some(resolved_type));
350+
// }
351+
352+
// Ok(None)
353+
}
305354
}
306355

307356
#[cfg(test)]

packages/fuels/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ fuel-core-client = { workspace = true, optional = true }
1818
fuel-crypto = { workspace = true }
1919
fuel-tx = { workspace = true }
2020
fuels-accounts = { workspace = true, default-features = false }
21+
fuels-code-gen = { workspace = true }
2122
fuels-core = { workspace = true }
2223
fuels-macros = { workspace = true }
2324
fuels-programs = { workspace = true }

packages/fuels/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ pub mod client {
2929
};
3030
}
3131

32+
pub mod code_gen {
33+
pub use fuels_code_gen::*;
34+
}
35+
3236
pub mod macros {
3337
pub use fuels_macros::*;
3438
}

0 commit comments

Comments
 (0)