gpui-form-derive 0.7.0

Macro crate for gpui-form
Documentation
use super::*;

pub(super) fn context_submit_registration_tokens(
    context: &DeriveContext,
    original_input: &DeriveInput,
    context_submit: Option<&McpContextSubmitOptions>,
) -> TokenStream {
    let Some(context_submit) = context_submit else {
        return quote! {};
    };

    let facade_crate = &context.paths.gpui_form;
    let original_ident = &original_input.ident;
    let context_type_id_fn_ident =
        format_ident!("__{}_gpui_form_mcp_context_submit_type_id", original_ident);
    let context_register_fn_ident =
        format_ident!("__{}_gpui_form_mcp_register_context_submit", original_ident);
    let context_definitions_fn_ident = format_ident!(
        "__{}_gpui_form_mcp_context_submit_tool_definitions",
        original_ident
    );
    let descriptor_fn_ident = format_ident!("__{}_gpui_form_mcp_descriptor", original_ident);
    let (impl_generics, ty_generics, where_clause) = original_input.generics.split_for_impl();
    let (context_type, response_type, context_submit_impl) = match context_submit {
        McpContextSubmitOptions::Explicit(context_submit) => {
            let context_type = &context_submit.context;
            let response_type = context_submit
                .response
                .as_ref()
                .map(|response_type| quote! { #response_type })
                .unwrap_or_else(|| quote! { #facade_crate::mcp::McpObject });
            let error_type = context_submit
                .error
                .as_ref()
                .map(|error_type| quote! { #error_type })
                .unwrap_or_else(|| quote! { String });
            let context_type = quote! { #context_type };
            let context_submit_impl = context_submit
                .submit
                .as_ref()
                .map(|submit| {
                    let submit_body =
                        if let Some(map_response) = context_submit.map_response.as_ref() {
                            quote! {
                                async move {
                                    let __gpui_form_response = (#submit)(self, context).await?;
                                    (#map_response)(__gpui_form_response)
                                }
                            }
                        } else {
                            quote! {
                                (#submit)(self, context)
                            }
                        };

                    quote! {
                        impl #impl_generics #facade_crate::mcp::McpContextSubmit<#context_type>
                            for #original_ident #ty_generics
                            #where_clause
                        {
                            type Response = #response_type;
                            type Error = #error_type;

                            fn submit_with_context(
                                self,
                                context: #context_type,
                            ) -> impl ::std::future::Future<
                                Output = Result<Self::Response, Self::Error>
                            > + Send + 'static {
                                #submit_body
                            }
                        }
                    }
                })
                .unwrap_or_else(|| quote! {});

            (context_type, response_type, context_submit_impl)
        },
        McpContextSubmitOptions::Submitter(context_submit) => {
            let submitter = &context_submit.submitter;
            let context_type = quote! { <#original_ident #ty_generics as #submitter>::Context };
            let submitter_response_type =
                quote! { <#original_ident #ty_generics as #submitter>::Response };
            let response_type = context_submit
                .response
                .as_ref()
                .map(|response_type| quote! { #response_type })
                .unwrap_or_else(|| {
                    if context_submit.map_response.is_some() {
                        quote! { #facade_crate::mcp::McpObject }
                    } else {
                        quote! { #submitter_response_type }
                    }
                });
            let error_type = quote! { <#original_ident #ty_generics as #submitter>::Error };
            let mut submitter_generics = original_input.generics.clone();
            {
                let where_clause = submitter_generics.make_where_clause();
                where_clause.predicates.push(syn::parse_quote! {
                    #original_ident #ty_generics: #submitter
                });
                where_clause.predicates.push(syn::parse_quote! {
                    #context_type: Clone + Send + Sync + 'static
                });
                where_clause.predicates.push(syn::parse_quote! {
                    #response_type: #facade_crate::mcp::Serialize + #facade_crate::mcp::McpJsonSchema
                });
                where_clause.predicates.push(syn::parse_quote! {
                    #error_type: ::core::fmt::Display
                });
            }
            let (submitter_impl_generics, _, submitter_where_clause) =
                submitter_generics.split_for_impl();
            let submit_body = if let Some(map_response) = context_submit.map_response.as_ref() {
                quote! {
                    async move {
                        let __gpui_form_response =
                            <#original_ident #ty_generics as #submitter>::submit_with_context(
                                self,
                                context,
                            )
                            .await?;
                        (#map_response)(__gpui_form_response)
                    }
                }
            } else {
                quote! {
                    <#original_ident #ty_generics as #submitter>::submit_with_context(
                        self,
                        context,
                    )
                }
            };
            let context_submit_impl = quote! {
                impl #submitter_impl_generics #facade_crate::mcp::McpContextSubmit<#context_type>
                    for #original_ident #ty_generics
                    #submitter_where_clause
                {
                    type Response = #response_type;
                    type Error = #error_type;

                    fn submit_with_context(
                        self,
                        context: #context_type,
                    ) -> impl ::std::future::Future<
                        Output = Result<Self::Response, Self::Error>
                    > + Send + 'static {
                        #submit_body
                    }
                }
            };

            (context_type, response_type, context_submit_impl)
        },
    };

    quote! {
        #context_submit_impl

        #[doc(hidden)]
        #[allow(non_snake_case)]
        fn #context_type_id_fn_ident() -> ::std::any::TypeId {
            ::std::any::TypeId::of::<#context_type>()
        }

        #[doc(hidden)]
        #[allow(non_snake_case)]
        fn #context_register_fn_ident(
            server: &mut #facade_crate::mcp::McpServer,
            context: #facade_crate::mcp::McpSubmitContext,
            options: #facade_crate::mcp::McpFormEditorOptions,
        ) -> Result<(), #facade_crate::mcp::McpToolError> {
            fn __gpui_form_mcp_context_submit_assert<T, Context, Response>()
            where
                T: #facade_crate::mcp::McpContextSubmit<Context, Response = Response>,
                Context: Clone + Send + Sync + 'static,
                Response: #facade_crate::mcp::Serialize + #facade_crate::mcp::McpJsonSchema,
            {}
            __gpui_form_mcp_context_submit_assert::<
                #original_ident #ty_generics,
                #context_type,
                #response_type,
            >();

            let context = context.downcast::<#context_type>().map_err(|_| {
                #facade_crate::mcp::McpToolError::validation(format!(
                    "context submitter for `{}` requires context `{}`",
                    stringify!(#original_ident),
                    ::std::any::type_name::<#context_type>(),
                ))
            })?;

            #facade_crate::mcp::form::<#original_ident #ty_generics>(server)
                .model_async_with_editor_options(options, move |request| {
                    let context = (*context).clone();
                    <
                        #original_ident #ty_generics
                        as #facade_crate::mcp::McpContextSubmit<#context_type>
                    >::submit_with_context(request, context)
                })
        }

        #[doc(hidden)]
        #[allow(non_snake_case)]
        fn #context_definitions_fn_ident(
        ) -> Result<Vec<#facade_crate::mcp::ToolDefinition>, #facade_crate::mcp::McpToolError> {
            fn __gpui_form_mcp_context_submit_assert<T, Context, Response>()
            where
                T: #facade_crate::mcp::McpContextSubmit<Context, Response = Response>,
                Context: Clone + Send + Sync + 'static,
                Response: #facade_crate::mcp::Serialize + #facade_crate::mcp::McpJsonSchema,
            {}
            __gpui_form_mcp_context_submit_assert::<
                #original_ident #ty_generics,
                #context_type,
                #response_type,
            >();
            <
                #original_ident #ty_generics as #facade_crate::mcp::McpSubmitArgument
            >::tool_definitions_with_editor::<#response_type>()
        }

        #facade_crate::mcp::registry::inventory::submit! {
            #facade_crate::mcp::registry::McpContextSubmitRegistration::new(
                #descriptor_fn_ident,
                #context_type_id_fn_ident,
                #context_register_fn_ident,
                #context_definitions_fn_ident,
            )
        }
    }
}