rialo-sol-syn 0.4.2

Sol syntax parsing and code generation tools
// Copyright (c) Subzero Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use heck::ToSnakeCase;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};

use super::{
    common::{gen_print_section, get_idl_module_path, get_serde_json_module_path},
    defined::gen_idl_type_def_struct,
};

pub fn gen_idl_print_fn_event(event_struct: &syn::ItemStruct) -> TokenStream {
    let idl = get_idl_module_path();
    let serde_json = get_serde_json_module_path();

    let ident = &event_struct.ident;
    let fn_name = format_ident!(
        "__sol_private_print_idl_event_{}",
        ident.to_string().to_snake_case()
    );
    let fn_name_test = format_ident!("{}__test", fn_name);
    let slice_entry = format_ident!(
        "__SOL_IDL_ENTRY_EVENT_{}",
        ident.to_string().to_snake_case().to_uppercase()
    );

    let idl_build_impl = impl_idl_build_event(event_struct);

    let print_ts = gen_print_section(
        "event",
        quote! {
            #serde_json::json!({
                "event": event,
                "types": types.into_values().collect::<Vec<_>>()
            })
        },
    );

    quote! {
        #idl_build_impl

        #[doc(hidden)]
        #[cfg(feature = "idl-build")]
        pub fn #fn_name() {
            let mut types: std::collections::BTreeMap<String, #idl::IdlTypeDef> =
                std::collections::BTreeMap::new();
            if let Some(event) = #ident::__sol_private_gen_idl_event(&mut types) {
                #print_ts
            }
        }

        #[cfg(feature = "idl-build")]
        #[allow(unsafe_code)]
        #[rialo_sol_lang::linkme::distributed_slice(rialo_sol_lang::__SOL_IDL_PRINT_FNS)]
        #[linkme(crate = rialo_sol_lang::linkme)]
        #[doc(hidden)]
        static #slice_entry: (fn(), &'static str) = (#fn_name, module_path!());

        #[cfg(feature = "idl-build")]
        #[test]
        fn #fn_name_test() {
            #fn_name();
        }
    }
}

/// Generate IDL build impl for an event.
fn impl_idl_build_event(event_struct: &syn::ItemStruct) -> TokenStream {
    let idl = get_idl_module_path();

    let ident = &event_struct.ident;
    let (impl_generics, ty_generics, where_clause) = event_struct.generics.split_for_impl();

    let fn_body = match gen_idl_type_def_struct(event_struct) {
        Ok((ts, defined)) => quote! {
            #(
                if let Some(ty) = <#defined>::create_type() {
                    types.insert(<#defined>::get_full_path(), ty);
                    <#defined>::insert_types(types);
                }
            );*

            let ty = #ts;
            let event = #idl::IdlEvent {
                name: ty.name.clone(),
                discriminator: Self::DISCRIMINATOR.into(),
            };
            types.insert(ty.name.clone(), ty);
            Some(event)
        },
        _ => quote! { None },
    };

    quote! {
        impl #impl_generics #ident #ty_generics #where_clause {
            pub fn __sol_private_gen_idl_event(
                types: &mut std::collections::BTreeMap<String, #idl::IdlTypeDef>,
            ) -> Option<#idl::IdlEvent> {
                #fn_body
            }
        }
    }
}