cido-macros 0.2.0

Macros for generating code that enables easier interfacing with cido
Documentation
use crate::parse::deps::*;
use proc_macro2::TokenStream;
use quote::{ToTokens, format_ident, quote};
use syn::Ident;
mod graphql_impl;

type Result = syn::Result<TokenStream>;

pub use crate::parse::{CidomapStruct, CidomapStructDef};

impl crate::codegen::CodegenWrapper<CidomapStruct> {
  pub fn codegen(&self) -> syn::Result<TokenStream> {
    let CidomapStruct { name, vis, .. } = &self.0;
    let mut token_stream = quote! {
      #[derive(::core::clone::Clone, ::core::fmt::Debug)]
      #vis struct #name;
    };

    let graphql_name = format_ident!("{name}Graphql");
    impl_cidomap(&self.0, &graphql_name)?.to_tokens(&mut token_stream);
    graphql_impl::graphql_impl(&self.0, &graphql_name)?.to_tokens(&mut token_stream);

    crate::parse::embed_generated_code(
      self.opts.embed_generated_code,
      name,
      token_stream,
      "cidomap",
    )
  }
}

fn impl_cidomap(cidomap: &CidomapStruct, graphql_name: &Ident) -> Result {
  let cidomap_name = &cidomap.name;
  let opts = &cidomap.opts;
  let fields = &cidomap.fields;

  let mut type_iter = Vec::new();
  let mut async_type_iter = Vec::new();
  for f in fields {
    let ty = &f.field.ty;
    type_iter.push(quote! {
      t.next_type::<#ty>()?;
    });
    async_type_iter.push(quote! {
      t.next_type::<#ty>().await?;
    })
  }

  let CidomapStructDef {
    start_block,
    max_block,
    max_processing_order,
    initial_filters,
    init,
    create,
    error,
    network,
    events,
  } = &opts.config;

  let output_init_fn = init
    .as_ref()
    .map(|init_fn| quote! {
      fn init<'a>(
        cx: ::cido::__internal::Context<'a, Self>,
      ) -> ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = ::std::result::Result<(), Self::Error>> + 'a>>
      {
        ::std::boxed::Box::pin(async move {#init_fn(cx).await })
      }
    }).unwrap_or_else(|| quote! {});

  let output_create_fn = create
    .as_ref()
    .map(|create_fn| quote! {
      fn create<'a>(
        cx: ::cido::__internal::Context<'a, Self>,
      ) -> ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = ::std::result::Result<(), Self::Error>> + 'a>>
      {
        ::std::boxed::Box::pin(async move {#create_fn(cx).await })
      }
    }).unwrap_or_else(|| quote! {});

  let max_processing_order = match *max_processing_order {
    1 => quote! {One},
    2 => quote! {Two},
    3 => quote! {Three},
    _ => {
      return Err(syn::Error::new(
        proc_macro2::Span::call_site(),
        format!("Invalid processing order {max_processing_order}"),
      ));
    }
  };
  let max_block = max_block
    .clone()
    .unwrap_or_else(|| syn::parse_quote!(::core::option::Option::None));

  Ok(quote! {
    impl ::cido::__internal::Cidomap for #cidomap_name {
      type Error = #error;
      type Network = #network;
      type Events = #events;
      type Graphql = #graphql_name;
      const MAX_PROCESSING_ORDER: ::cido::__internal::ProcessingOrder = ::cido::__internal::ProcessingOrder::#max_processing_order;
      const START_BLOCK: <Self::Network as ::cido::__internal::Network>::BlockNumber = #start_block;
      const MAX_BLOCK: ::core::option::Option<<Self::Network as ::cido::__internal::Network>::BlockNumber> = #max_block;

      fn type_iter<T: ::cido::__internal::TypeIter<Self>>(t: &mut T) -> ::std::result::Result<(), ::cido::__internal::CidomapError> {
        #(#type_iter)*
        ::std::result::Result::Ok(())
      }

      fn async_type_iter<T: ::cido::__internal::AsyncTypeIter<Self>>(t: &mut T) -> ::cido::__internal::futures::future::BoxFuture<'_, ::std::result::Result<(), ::cido::__internal::CidomapError>> {
        ::std::boxed::Box::pin(async move {
          #(#async_type_iter)*
          ::std::result::Result::Ok(())
        })
      }

      fn initial_filters(
      ) -> ::std::result::Result<::std::vec::Vec<<Self::Network as ::cido::__internal::Network>::TriggerFilter>, <Self::Network as ::cido::__internal::Network>::Error> {
        #initial_filters()
      }

      #output_init_fn
      #output_create_fn
    }

    pub fn main() -> ::core::result::Result<(), ::cido::__internal::CidomapError> {
      ::cido::__internal::run::<#cidomap_name>()
    }
  })
}