sark-gen 0.4.0

Sark proc-macro generators
Documentation
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::Result;

use crate::codegen::header::BytesMatch;
use crate::codegen::value::{QueryScanLoop, Value};
use crate::model::QueryAttrField;
use crate::util::TypeExt;

pub(super) struct Query<'a> {
    fields: &'a [QueryAttrField],
}

impl<'a> Query<'a> {
    pub(super) fn new(fields: &'a [QueryAttrField]) -> Self {
        Self { fields }
    }

    pub(super) fn set_name_direct(&self) -> Result<TokenStream> {
        self.states_loop(|_| Ok(quote! { Some(sark::service::FieldValue::parse_value(value)?) }))
    }

    pub(super) fn set_slice_direct(&self) -> Result<TokenStream> {
        self.states_loop(|field| {
            Ok(Value::build_parse_expr(
                field.ty.value_kind()?,
                quote!(range.clone()),
            ))
        })
    }

    pub(super) fn parse_direct() -> Result<TokenStream> {
        let per_segment = quote! {
            Self::set_query_slice_raw(
                headers,
                name,
                input,
                value_start_abs..value_end_abs,
            )?;
        };
        Ok(QueryScanLoop::build(&per_segment))
    }

    fn skippable(field: &QueryAttrField) -> bool {
        field.ty.value_optional() || field.default.is_some()
    }

    fn states_loop<F>(&self, assign_for: F) -> Result<TokenStream>
    where
        F: Fn(&QueryAttrField) -> Result<TokenStream>,
    {
        let states: Vec<_> = self
            .fields
            .iter()
            .enumerate()
            .map(|(idx, field)| {
                let ident = &field.ident;
                let name =
                    BytesMatch::exact(&format_ident!("name"), field.query.value().as_bytes());
                let state = idx as u8;
                let next = state.saturating_add(1);
                let assign = assign_for(field)?;
                let miss = if Self::skippable(field) {
                    quote! {
                        headers.__query = #next;
                        continue;
                    }
                } else {
                    quote! {
                        return Err(sark_core::error::Error::BadRequest(
                            "Invalid query field".into(),
                        ));
                    }
                };
                Ok(quote! {
                    #state => {
                        if #name {
                            headers.#ident = #assign;
                            headers.__query = #next;
                            return Ok(());
                        }
                        #miss
                    }
                })
            })
            .collect::<Result<_>>()?;
        Ok(quote! {
            loop {
                match headers.__query {
                    #( #states, )*
                    _ => {
                        return Err(sark_core::error::Error::BadRequest(
                            "Invalid query field".into(),
                        ));
                    }
                }
            }
        })
    }
}