Skip to content
This repository has been archived by the owner on Oct 25, 2024. It is now read-only.

Commit

Permalink
enhancement: support for Sway array types (#1411)
Browse files Browse the repository at this point in the history
* enhancement: support for Sway array types

* update test

* deekerno feedback
  • Loading branch information
ra0x3 authored Oct 17, 2023
1 parent 1869504 commit 0eb480f
Show file tree
Hide file tree
Showing 9 changed files with 298 additions and 189 deletions.
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

0 comments on commit 0eb480f

Please sign in to comment.