Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhancement: support for Sway array types #1411

Merged
merged 3 commits into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions packages/fuel-indexer-lib/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,4 @@ lazy_static! {
"Vec",
"Option"
]);

/// Set of Rust primitive types.
pub static ref RUST_PRIMITIVES: HashSet<&'static str> =
HashSet::from(["u8", "u16", "u32", "u64", "bool", "String"]);

}
84 changes: 67 additions & 17 deletions packages/fuel-indexer-macros/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ pub fn is_non_decodable_type(typ: &TypeDeclaration) -> bool {
is_tuple_type(typ)
|| is_unit_type(typ)
|| IGNORED_GENERIC_METADATA.contains(typ.type_field.as_str())
|| is_array_type(typ)
}

/// Derive Ident for decoded type
Expand All @@ -105,7 +104,13 @@ fn decoded_ident(typ: &TypeDeclaration) -> Ident {
let name = {
let name = derive_type_name(typ);
if typ.components.is_some() {
name
if is_array_type(typ) {
let name = name.replace(['[', ']', ' '], "");
let name = name.replace(';', "_");
format!("array_{}", name)
} else {
name
}
} else if name.starts_with("Option") {
typ.type_field.replace(['<', '>'], "_")
} else {
Expand Down Expand Up @@ -138,11 +143,15 @@ fn decoded_ident(typ: &TypeDeclaration) -> Ident {
///
/// `Vec<T>` returns `Vec`, `Option<T>` returns `Option`, `u8` returns `u8`, etc.
pub fn derive_type_name(typ: &TypeDeclaration) -> String {
typ.type_field
.split(' ')
.last()
.expect("Type field name expected")
.to_string()
if is_array_type(typ) {
typ.type_field.clone()
} else {
typ.type_field
.split(' ')
.last()
.expect("Type field name expected")
.to_string()
}
}

/// Whether or not the given token is a Fuel primitive
Expand All @@ -156,9 +165,16 @@ pub fn is_fuel_primitive(typ: &TypeDeclaration) -> bool {
}

/// Whether or not the given token is a Rust primitive
pub fn is_rust_primitive(ty: &proc_macro2::TokenStream) -> bool {
fn is_rust_primitive(ty: &proc_macro2::TokenStream) -> bool {
let ident_str = ty.to_string();
RUST_PRIMITIVES.contains(ident_str.as_str())
match ident_str.as_str() {
"u8" | "u16" | "u32" | "u64" | "bool" | "String" => true,
_ => {
ident_str.starts_with('[')
&& ident_str.ends_with(']')
&& ident_str.contains(';')
}
}
}

/// Whether or not the given tokens are a generic type
Expand Down Expand Up @@ -245,7 +261,22 @@ impl Codegen for TypeDeclaration {
}

fn rust_tokens(&self) -> proc_macro2::TokenStream {
if self.components.is_some() {
// Array works a bit differently where it's not a complex type (it's a rust primitive),
// but we still have to format each part of the array (type and size) separately.
if is_array_type(self) {
let name = derive_type_name(self).replace(['[', ']', ';'], "");
let mut iter = name.split(' ');
let ty = iter.next().expect("Malformed derived array type name.");
let size = iter
.next()
.expect("Array type name malformed.")
.parse::<usize>()
.expect("Could not parse array size.");

let ty = format_ident! { "{}", ty };

quote! { [#ty; #size] }
} else if self.components.is_some() {
let name = derive_type_name(self);
let ident = format_ident! { "{}", name };
quote! { #ident }
Expand Down Expand Up @@ -849,7 +880,11 @@ pub fn derive_log_generic_inner_typedefs<'a>(
.flatten()
.filter_map(|log| {
if log.log_id == typ.log_id && log.application.type_arguments.is_some() {
let args = log.application.type_arguments.as_ref().unwrap();
let args = log
.application
.type_arguments
.as_ref()
.expect("No type args found for log application.");
let inner = args.first().expect("No type args found.");
return Some(abi_types.get(&inner.type_id).unwrap_or_else(|| {
panic!("Inner type not in ABI: {:?}", inner)
Expand All @@ -875,7 +910,11 @@ pub fn derive_generic_inner_typedefs<'a>(
log_types: &[LoggedType],
abi_types: &'a HashMap<usize, TypeDeclaration>,
) -> Vec<&'a TypeDeclaration> {
let name = typ.type_field.split(' ').last().unwrap();
let name = typ
.type_field
.split(' ')
.last()
.expect("Type name derivation expects a space.");
let t = GenericType::from(name);

// Per Ahmed from fuels-rs:
Expand All @@ -893,7 +932,10 @@ pub fn derive_generic_inner_typedefs<'a>(
.iter()
.filter_map(|i| {
if i.type_id == typ.type_id && i.type_arguments.is_some() {
let args = i.type_arguments.as_ref().unwrap();
let args = i
.type_arguments
.as_ref()
.expect("No type args found for function input.");
let inner = args.first().expect("No type args found.");
return Some(
abi_types.get(&inner.type_id).unwrap_or_else(|| {
Expand All @@ -913,7 +955,11 @@ pub fn derive_generic_inner_typedefs<'a>(
if func.output.type_id == typ.type_id
&& func.output.type_arguments.is_some()
{
let args = func.output.type_arguments.as_ref().unwrap();
let args = func
.output
.type_arguments
.as_ref()
.expect("No type args found for function output.");
let inner = args.first().expect("No type args found.");
return Some(abi_types.get(&inner.type_id).unwrap_or_else(
|| panic!("Inner type not in ABI: {:?}", inner),
Expand All @@ -932,7 +978,11 @@ pub fn derive_generic_inner_typedefs<'a>(
if log.application.type_id == typ.type_id
&& log.application.type_arguments.is_some()
{
let args = log.application.type_arguments.as_ref().unwrap();
let args = log
.application
.type_arguments
.as_ref()
.expect("No type args found for log application.");
let inner = args.first().expect("No type args found.");
return Some(abi_types.get(&inner.type_id).unwrap_or_else(
|| panic!("Inner type not in ABI: {:?}", inner),
Expand Down Expand Up @@ -1051,7 +1101,7 @@ pub fn function_output_type_id(
.output
.type_arguments
.as_ref()
.unwrap()
.expect("Missing type arguments.")
.first()
.expect("Missing inner type.");

Expand Down Expand Up @@ -1117,7 +1167,7 @@ pub fn typed_path_components(
(name, tokens)
}

fn is_array_type(typ: &TypeDeclaration) -> bool {
pub fn is_array_type(typ: &TypeDeclaration) -> bool {
typ.type_field.starts_with('[')
&& typ.type_field.ends_with(']')
&& typ.type_field.contains(';')
Expand Down
50 changes: 48 additions & 2 deletions packages/fuel-indexer-macros/src/indexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,48 @@ fn process_fn_items(
}])
.collect::<Vec<proc_macro2::TokenStream>>();

// Take a second pass over `abi_types` in order to update an `TypeDeclarations` that need to
// be updated with more information for the codegen process.
let abi_types = abi_types
.iter()
.map(|typ| {
// If this is an array type we have to manually update it's type field to include its inner
// type so that the rest of the codegen will work with minimal changes.
if is_array_type(typ) {
let inner = typ
.components
.as_ref()
.expect("Array type expects inner components")
.first()
.expect("Array type expects at least one inner component")
.type_id;
let size = typ
.type_field
.split(' ')
.last()
.expect(
"Array type has unexpected type field format. Expected [u8; 32]",
)
.trim_end_matches(']')
.parse::<usize>()
.expect("Array type size could not be determined.");
let inner = abi_types_tyid
.get(&inner)
.expect("Array type inner not found in ABI types.");
let name = format!("[{}; {}]", inner.name(), size);
let typ = TypeDeclaration {
type_field: name,
..typ.clone()
};

abi_types_tyid.insert(typ.type_id, typ.clone());
typ
} else {
typ.to_owned()
}
})
.collect::<Vec<TypeDeclaration>>();

let abi_type_decoders = abi_types
.iter()
.filter_map(|typ| {
Expand Down Expand Up @@ -298,7 +340,9 @@ fn process_fn_items(
.filter_map(|log| {
let ty_id = log.application.type_id;
let log_id = log.log_id as usize;
let typ = abi_types_tyid.get(&log.application.type_id).unwrap();
let typ = abi_types_tyid
.get(&log.application.type_id)
.expect("Could not get log type reference from ABI types.");

if is_non_decodable_type(typ) {
return None;
Expand Down Expand Up @@ -492,7 +536,9 @@ fn process_fn_items(
);
};

let ty_id = type_ids.get(&path_type_name).unwrap();
let ty_id = type_ids
.get(&path_type_name)
.expect("Path type name not found in type IDs.");
let typ = match abi_types_tyid.get(ty_id) {
Some(typ) => typ,
None => fuel_types.get(ty_id).unwrap(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[[package]]
name = 'core'
source = 'path+from-root-EB296BD18C0E4CC4'
source = 'path+from-root-63B5D95B29B128A3'

[[package]]
name = 'fuel-indexer-test'
Expand All @@ -9,5 +9,5 @@ dependencies = ['std']

[[package]]
name = 'std'
source = 'git+https://github.com/fuellabs/sway?tag=v0.44.1#04a597093e7441898933dd412b8e4dc6ac860cd3'
source = 'git+https://github.com/fuellabs/sway?tag=v0.45.0#92dc9f361a9508a940c0d0708130f26fa044f6b3'
dependencies = ['core']
Loading
Loading