sark-gen 0.26.3

Sark proc-macro generators
Documentation
use decode::state;
use handle::decode;

use crate::{
    app::{
        serve::{self, handle},
        spec,
    },
    macros::tokens::local,
    runtime::paths::Paths,
};

pub(super) fn generate(
    paths: &Paths,
    ctx: &handle::Context<'_, '_, '_>,
    state: &state::State,
) -> Vec<proc_macro2::TokenStream> {
    let [f_core, f_state] = ["core", "state"].map(local);
    let symbols = &ctx.plan.spec.state_generics;
    let [n_body, n_call] = symbols.locals(["__body", "__call"]);
    let [n_sess, v_cause] = symbols.locals(["__session", "cause"]);
    let [v_len, v_head] = symbols.locals(["content_length", "head"]);
    use quote::{format_ident, quote};

    let ns_core = &paths.core;

    let prep = &state.prepared;
    let error = format_ident!("{}ProjectError", ctx.plan.spec.name);
    let state_ty = ctx.plan.spec.state.ty();
    let rejects = ctx
        .plan
        .entries
        .iter()
        .filter(|item| item.kind != spec::Kind::Duplex || item.entry.upgrade)
        .map(|item| {
            let variant = &item.prepared;
            quote! {
                #prep::#variant(_) =>
                    ::core::result::Result::Err(
                        #ns_core::duplex::OpenError::Unsupported,
                    )
            }
        });
    rejects
        .chain(
            ctx.plan
                .entries
                .iter()
                .filter_map(|item| match item.mode {
                    serve::Mode::Duplex { family } if !item.entry.upgrade => Some((item, family)),
                    _ => None,
                })
                .map(|(item, family)| {
                    let entry = item.entry;
                    let variant = &item.prepared;
                    let slot = &item.request;
                    let route = &entry.route;
                    let wrapped = ctx.plan.duplex.wrap(family, true, quote!(#n_sess));
                    quote! {
                        #prep::#variant(#slot {
                            head: #v_head,
                            content_length: #v_len,
                        }) => {
                            match #v_len {
                                ::core::option::Option::Some(0)
                                | ::core::option::Option::None => {}
                                ::core::option::Option::Some(1..) => {
                                    return ::core::result::Result::Err(
                                        #ns_core::duplex::OpenError::Length,
                                    );
                                }
                            }
                            let #n_body =
                                <<<#route as #ns_core::route::Spec>::Request
                                    as #ns_core::route::Schema>::Body
                                    as #ns_core::body::Project>::empty::<'_, 'd>();
                            let #n_call = #ns_core::decode::Call::new(
                                #v_head.borrow(),
                                #n_body,
                            );
                            let #n_sess = #n_call.try_open::<#route, #state_ty>(
                                self.#f_core.#f_state,
                            ).map_err(|#v_cause| {
                                #ns_core::duplex::OpenError::InvalidRequest(
                                    #error::#variant(#v_cause),
                                )
                            })?;
                            ::core::result::Result::Ok(#wrapped)
                        }
                    }
                }),
        )
        .collect()
}