sword-macros 0.2.1

Structured web framework built on top of tokio ecosystem, providing powerful features for building robust web applications.
Documentation
use crate::shared::{CommonHttpAdapterInput, gen_build, gen_clone, gen_deps};

use proc_macro2::TokenStream;
use quote::quote;
use syn::Path;

pub fn generate_socketio_adapter_builder(
    input: &CommonHttpAdapterInput,
    interceptors: &[Path],
) -> TokenStream {
    let namespace = &input.base_path;
    let self_name = &input.struct_name;
    let self_fields = &input.fields;
    let adapter_name_str = self_name.to_string();

    let deps_impl = gen_deps(self_name, self_fields);
    let build_impl = gen_build(self_name, self_fields);
    let clone_impl = gen_clone(self_name, self_fields);

    let interceptor_applications = interceptors.iter().map(|interceptor_path| {
        quote! {
            let interceptor = ::std::sync::Arc::new(
                state.borrow::<#interceptor_path>()
                    .expect(&format!("\n[!] Failed to retrieve interceptor {} from State\n", stringify!(#interceptor_path)))
                    .clone()
            );

            let handler = handler.with(move |ctx: ::sword::prelude::SocketContext| {
                let interceptor = ::std::sync::Arc::clone(&interceptor);
                async move {
                    <#interceptor_path as ::sword::prelude::OnConnect>::on_connect(&*interceptor, ctx)
                        .await
                        .map_err(|e| ::std::boxed::Box::new(e) as ::std::boxed::Box<dyn ::std::fmt::Display + Send>)
                }
            });
        }
    });

    let setup_impl = quote! {
        #[doc(hidden)]
        pub fn __socketio_setup(state: &::sword::internal::core::State) {
            use ::sword::internal::socketio::ConnectHandler;

            let adapter = ::std::sync::Arc::new(
                <#self_name as ::sword::internal::core::Build>::build(state).unwrap_or_else(|err| {
                    eprintln!("\n[!] Failed to build SocketIO adapter: {}\n", #adapter_name_str);
                    eprintln!("    Error: {}\n", err);
                    eprintln!("    This usually means a dependency is missing from the State.");
                    eprintln!("    Ensure all dependencies are registered as providers or components.\n");
                    panic!("SocketIO adapter build failed");
                })
            );

            let io = <::sword::prelude::SocketIo as ::sword::internal::core::FromState>::from_state(state)
                .expect("\n[!] SocketIo component not found in state. Is SocketIo correctly configured?\n\n   ↳ Ensure that the `socketio` feature is enabled in your `Cargo.toml`.\n   ↳ Also, make sure to configure the `socketio` server in your configuration file.\n   ↳ See the Sword documentation for more details: https://sword-web.github.io\n");

            let adapter_type_id = ::std::any::TypeId::of::<#self_name>();
            let mut connection_handler: ::std::option::Option<::sword::internal::socketio::HandlerRegistrar> = None;
            let mut message_handlers = ::std::vec::Vec::new();

            for handler_meta in ::sword::internal::inventory::iter::<::sword::internal::socketio::HandlerRegistrar>() {
                if handler_meta.adapter_type_id != adapter_type_id {
                    continue;
                }

                match handler_meta.event_kind {
                    ::sword::internal::socketio::SocketEventKind::Connection => {
                        if connection_handler.is_some() {
                            panic!(
                                "\n[!] Multiple connection handlers found in adapter '{}'\n\n   ↳ Only one #[on(\"connection\")] handler is allowed per adapter\n",
                                #adapter_name_str
                            );
                        }
                        connection_handler = Some(handler_meta.clone());
                    },
                    ::sword::internal::socketio::SocketEventKind::Message(_)
                        | ::sword::internal::socketio::SocketEventKind::Disconnection
                        | ::sword::internal::socketio::SocketEventKind::Fallback => {
                        message_handlers.push(handler_meta.clone());
                    }
                }
            }

            let message_handlers: ::std::sync::Arc<[::sword::internal::socketio::HandlerRegistrar]> =
                ::std::sync::Arc::from(message_handlers.into_boxed_slice());

            let base_handler = move |ctx: ::sword::prelude::SocketContext| -> ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = ()> + ::std::marker::Send>> {
                let adapter = adapter.clone();
                let socket = ctx.socket.clone();
                let connection_handler = connection_handler.clone();
                let message_handlers = message_handlers.clone();

                ::std::boxed::Box::pin(async move {
                    if let Some(handler) = connection_handler {
                        (handler.call_fn)(adapter.clone(), ctx).await;
                    }

                    for handler in message_handlers.iter() {
                        (handler.register_fn)(adapter.clone(), socket.clone());
                    }
                })
            };

            let handler = base_handler;
            #(#interceptor_applications)*

            io.ns(#namespace, handler);
        }
    };

    let setup_registration = quote! {
        const _: () = {
            ::sword::internal::inventory::submit! {
                ::sword::internal::socketio::SocketIoHandlerRegistrar {
                    handler_type_id: ::std::any::TypeId::of::<#self_name>(),
                    handler_type_name: stringify!(#self_name),
                    setup_fn: #self_name::__socketio_setup,
                }
            }
        };
    };

    quote! {
        #build_impl
        #deps_impl
        #clone_impl

        impl ::sword::internal::core::SocketIoAdapter for #self_name {
            fn namespace() -> &'static str {
                #namespace
            }
        }

        impl ::sword::internal::core::Adapter for #self_name {
            fn kind() -> ::sword::internal::core::AdapterKind {
                ::sword::internal::core::AdapterKind::SocketIo
            }

            fn type_id() -> ::std::any::TypeId {
                ::std::any::TypeId::of::<Self>()
            }
        }

        impl #self_name {
            #setup_impl
        }

        #setup_registration
    }
}