sark-gen 0.26.2

Sark proc-macro generators
Documentation
use crate::{macros::tokens::local, runtime::paths::Paths};

use super::Source;

mod json;
mod retain;

pub(in crate::request) struct Names<'tokens> {
    pub(in crate::request) request: &'tokens syn::Ident,
    pub(in crate::request) builder: &'tokens syn::Ident,
    pub(in crate::request) params: &'tokens proc_macro2::TokenStream,
    pub(in crate::request) headers: &'tokens proc_macro2::TokenStream,
}

pub(in crate::request) struct Output {
    pub(in crate::request) declaration: proc_macro2::TokenStream,
    pub(in crate::request) definition: proc_macro2::TokenStream,
    pub(in crate::request) reject: proc_macro2::TokenStream,
    pub(in crate::request) ty: proc_macro2::TokenStream,
    pub(in crate::request) start: proc_macro2::TokenStream,
}

fn ready(paths: &Paths, task: &Task<'_>) -> proc_macro2::TokenStream {
    let ns_core = &paths.core;
    let request = task.names.request;
    let fields = task.fields;
    let [v_auth, v_params, v_heads, v_len, v_id, v_credit] = [
        "__sark_authority",
        "params",
        "headers",
        "__sark_body_len",
        "__sark_id",
        "__sark_released",
    ]
    .map(local);
    let take = task.authority.map(|peer| {
        quote::quote! {
            let #v_auth: #peer = self.authority;
        }
    });
    quote::quote! {
        let #v_params = self.params;
        let #v_heads = self.headers;
        #take
        let #v_len = self.body_len;
        let #v_id = self.id;
        #ns_core::body::Step::Ready {
            output: #ns_core::route::Built::new(
                #v_id,
                #request { #(#fields,)* },
            ),
            released: #v_credit,
        }
    }
}

use retain::Retain;

use crate::request::params;

pub(in crate::request) struct Task<'a> {
    pub(in crate::request) names: Names<'a>,
    pub(in crate::request) authority: Option<&'a syn::Type>,
    pub(in crate::request) params: &'a params::Expansion,
    pub(in crate::request) head: &'a proc_macro2::TokenStream,
    pub(in crate::request) fields: &'a [proc_macro2::TokenStream],
    pub(in crate::request) vis: &'a syn::Visibility,
}

impl Task<'_> {
    pub(super) fn own(&self) -> proc_macro2::TokenStream {
        let head = self.head;
        let [id, request, body, storage, len] = [
            "id",
            "request",
            "body",
            "__sark_body_storage",
            "__sark_body_len",
        ]
        .map(local);
        quote::quote! {
            let #id = #request.owner().id();
            let (#request, #body) = #request.into_parts();
            #head
            let (#storage, #len) = #body.into_parts();
        }
    }

    pub(super) fn empty(&self) -> proc_macro2::TokenStream {
        let head = self.head;
        let [id, request, body, len] = ["id", "request", "body", "__sark_body_len"].map(local);
        quote::quote! {
            let #id = #request.owner().id();
            let (#request, #body) = #request.into_parts();
            #head
            let #len = #body.len();
        }
    }

    pub(super) fn start(
        &self,
        paths: &Paths,
        own: &proc_macro2::TokenStream,
        body_err: proc_macro2::TokenStream,
        body_rej: proc_macro2::TokenStream,
        own_add: proc_macro2::TokenStream,
        feed_add: proc_macro2::TokenStream,
    ) -> proc_macro2::TokenStream {
        let ns_core = &paths.core;
        let ns_http = &paths.http;
        let builder = self.names.builder;
        let [
            v_auth,
            v_owned,
            v_id,
            v_rqst,
            v_params,
            v_heads,
            v_len,
            v_err,
        ] = [
            "authority",
            "__sark_authority",
            "id",
            "request",
            "params",
            "headers",
            "__sark_body_len",
            "error",
        ]
        .map(local);
        let (frame, init) = match self.authority {
            Some(peer) => (
                quote::quote!(#ns_core::request::frame::proof::Peer),
                quote::quote! {
                    authority: {
                        let #v_auth: #peer = #v_owned;
                        #v_auth
                    },
                },
            ),
            None => (
                quote::quote!(#ns_core::request::frame::proof::Maybe),
                proc_macro2::TokenStream::new(),
            ),
        };
        let error = &self.params.error;
        let head = self.head;
        let reject = &self.params.reject;
        let feed = quote::quote! {
            let #v_id = #v_rqst.owner().id();
            #head
        };
        quote::quote! {
            type Error<'d> = #ns_core::route::Input<
                #error, #body_err, #ns_http::o3::mem::rc::Error,
            >;

            fn reject<'d>(
                #v_err: <Self as #ns_core::route::Own>::Error<'d>,
            ) -> #ns_core::route::Reject {
                match #v_err {
                    #ns_core::route::Input::Params(#v_err) => #reject,
                    #ns_core::route::Input::Body(#v_err) => #body_rej,
                    #ns_core::route::Input::Retain(
                        #ns_http::o3::mem::rc::Error::Exhausted,
                    ) => #ns_core::route::Reject::Unavailable,
                }
            }

            fn start_task<'d>(
                #v_rqst: #ns_core::request::raw::Parsed<
                    #ns_core::request::Retained<'d, #frame>, Self,
                >,
            ) -> ::core::result::Result<
                Self::TaskBuilder<'d>,
                <Self as #ns_core::route::Own>::Error<'d>,
            > {
                #own
                ::core::result::Result::Ok(#builder {
                    id: #v_id,
                    params: #v_params,
                    headers: #v_heads,
                    #init
                    body_len: #v_len,
                    #own_add
                })
            }

            fn start_stream<'d>(
                #v_rqst: #ns_core::request::raw::Parsed<
                    #ns_core::request::Parts<'d, #frame>, Self,
                >,
            ) -> ::core::result::Result<
                Self::TaskBuilder<'d>,
                <Self as #ns_core::route::Own>::Error<'d>,
            > {
                #feed
                ::core::result::Result::Ok(#builder {
                    id: #v_id,
                    params: #v_params,
                    headers: #v_heads,
                    #init
                    body_len: 0,
                    #feed_add
                })
            }
        }
    }

    pub(in crate::request) fn build(self, paths: &Paths, source: &Source) -> Output {
        let ns_http = &paths.http;
        let builder = self.names.builder;
        let params = self.names.params;
        let headers = self.names.headers;
        let field = self.authority.map(|peer| quote::quote!(authority: #peer,));
        let vis = self.vis;
        let mut output = match source {
            Source::Json(json) => json::build(self, paths, &json.ty),
            Source::Raw(raw) => Retain {
                task: self,
                segmented: raw.segmented,
            }
            .build(paths),
            Source::Discarded => self.discard(paths),
        };
        let fields = output.declaration;
        output.declaration = quote::quote! {
            #[doc(hidden)]
            #vis struct #builder<'d> {
                id: #ns_http::request::Id<'d>,
                params: #params,
                headers: #headers,
                #field
                body_len: ::core::primitive::usize,
                #fields
            }
        };
        output
    }

    fn discard(self, paths: &Paths) -> Output {
        let ns_core = &paths.core;
        let ns_http = &paths.http;
        let [error, len, released] = ["error", "body_len", "__sark_released"].map(local);
        let ready = ready(paths, &self);
        let own = self.empty();
        let start = self.start(
            paths,
            &own,
            quote::quote!(::core::convert::Infallible),
            quote::quote!(match #error {}),
            quote::quote!(input_finished: true,),
            quote::quote!(input_finished: false,),
        );
        let request = self.names.request;
        let builder = self.names.builder;
        Output {
            declaration: quote::quote!(input_finished: ::core::primitive::bool,),
            definition: quote::quote! {
                impl<'d> #ns_core::route::TaskBuild<'d> for #builder<'d> {
                    type Task = #request<'d>;

                    fn reject(#error: Self::StepError) -> #ns_core::route::Reject {
                        match #error {}
                    }
                }

                impl<'d> #ns_core::body::Build<'d> for #builder<'d> {
                    type Output = #ns_core::route::Built<'d, #request<'d>>;
                    type StepError = ::core::convert::Infallible;

                    fn finish_input(mut self, #len: ::core::primitive::usize) -> Self {
                        self.body_len = #len;
                        self.input_finished = true;
                        self
                    }

                    fn step(
                        self,
                        _: ::core::num::NonZero<::core::primitive::usize>,
                    ) -> #ns_core::body::Step<
                        Self,
                        Self::Output,
                        Self::StepError,
                    > {
                        let #released =
                            #ns_core::body::credit::Amount::new(
                                0,
                                #ns_http::o3::buffer::segments::Count::EMPTY,
                            );
                        if !self.input_finished {
                            return #ns_core::body::Step::NeedInput {
                                build: self,
                                released: #released,
                            };
                        }
                        #ready
                    }
                }
            },
            reject: quote::quote!(match #error {}),
            ty: quote::quote!(#builder<'d>),
            start,
        }
    }
}