fuels-code-gen 0.77.0

Used for code generation in the Fuel Rust SDK
Documentation
use fuel_abi_types::abi::full_program::FullConfigurable;
use proc_macro2::{Ident, TokenStream};
use quote::quote;

use crate::{
    error::Result,
    program_bindings::resolved_type::{ResolvedType, TypeResolver},
    utils::safe_ident,
};

#[derive(Debug)]
pub(crate) struct ResolvedConfigurable {
    pub name: Ident,
    pub ttype: ResolvedType,
    pub offset: u64,
}

impl ResolvedConfigurable {
    pub fn new(configurable: &FullConfigurable) -> Result<ResolvedConfigurable> {
        let type_application = &configurable.application;
        Ok(ResolvedConfigurable {
            name: safe_ident(&format!("with_{}", configurable.name)),
            ttype: TypeResolver::default().resolve(type_application)?,
            offset: configurable.offset,
        })
    }
}

pub(crate) fn generate_code_for_configurable_constants(
    configurable_struct_name: &Ident,
    configurables: &[FullConfigurable],
) -> Result<TokenStream> {
    let resolved_configurables = configurables
        .iter()
        .map(ResolvedConfigurable::new)
        .collect::<Result<Vec<_>>>()?;

    let struct_decl = generate_struct_decl(configurable_struct_name);
    let struct_impl = generate_struct_impl(configurable_struct_name, &resolved_configurables);
    let from_impl = generate_from_impl(configurable_struct_name);

    Ok(quote! {
        #struct_decl
        #struct_impl
        #from_impl
    })
}

fn generate_struct_decl(configurable_struct_name: &Ident) -> TokenStream {
    quote! {
        #[derive(Clone, Debug, Default)]
        pub struct #configurable_struct_name {
            offsets_with_data: ::std::vec::Vec<::fuels::core::Configurable>,
            encoder: ::fuels::core::codec::ABIEncoder,
        }
    }
}

fn generate_struct_impl(
    configurable_struct_name: &Ident,
    resolved_configurables: &[ResolvedConfigurable],
) -> TokenStream {
    let builder_methods = generate_builder_methods(resolved_configurables);

    quote! {
        impl #configurable_struct_name {
            pub fn new(encoder_config: ::fuels::core::codec::EncoderConfig) -> Self {
                Self {
                    encoder: ::fuels::core::codec::ABIEncoder::new(encoder_config),
                    ..::std::default::Default::default()
                }
            }

            #builder_methods
        }
    }
}

fn generate_builder_methods(resolved_configurables: &[ResolvedConfigurable]) -> TokenStream {
    let methods = resolved_configurables.iter().map(
        |ResolvedConfigurable {
             name,
             ttype,
             offset,
         }| {
            let encoder_code = generate_encoder_code(ttype);
            quote! {
                #[allow(non_snake_case)]
                // Generate the `with_XXX` methods for setting the configurables
                pub fn #name(mut self, value: #ttype) -> ::fuels::prelude::Result<Self> {
                    let encoded = #encoder_code?;
                    self.offsets_with_data.push(::fuels::core::Configurable {
                        offset: #offset,
                        data: encoded,
                    });
                    ::fuels::prelude::Result::Ok(self)
                }
            }
        },
    );

    quote! {
        #(#methods)*
    }
}

fn generate_encoder_code(ttype: &ResolvedType) -> TokenStream {
    quote! {
        self.encoder.encode(&[
                <#ttype as ::fuels::core::traits::Tokenizable>::into_token(value)
            ])
    }
}

fn generate_from_impl(configurable_struct_name: &Ident) -> TokenStream {
    quote! {
        impl From<#configurable_struct_name> for ::fuels::core::Configurables {
            fn from(config: #configurable_struct_name) -> Self {
                ::fuels::core::Configurables::new(config.offsets_with_data)
            }
        }
        impl From<#configurable_struct_name> for ::std::vec::Vec<::fuels::core::Configurable> {
            fn from(config: #configurable_struct_name) -> ::std::vec::Vec<::fuels::core::Configurable> {
                config.offsets_with_data
            }
        }
    }
}