1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
|
use proc_macro2::{Ident, TokenStream};
use quote::quote;
use syn::{ItemFn, LitStr};
use crate::common::extract::FnArgument;
use super::extract::{extract_components, Components};
pub(crate) struct Expanded {
pub(crate) name_str: LitStr,
pub(crate) validator: TokenStream,
pub(crate) validator_name: Ident,
pub(crate) evaluator: TokenStream,
pub(crate) evaluator_name: Ident,
pub(crate) result: TokenStream,
pub(crate) core: TokenStream,
}
/// Expand the macro input to produce the common elements used in the `#[function]` and
/// `#[register]` macros.
pub(crate) fn expand(input: ItemFn, name_str: Option<LitStr>) -> Result<Expanded, TokenStream> {
let ItemFn {
attrs: _,
vis: _,
sig,
block,
} = input;
let Components {
name,
generics,
validator_name,
evaluator_name,
result,
args,
ret,
inputs,
} = match extract_components(sig) {
Ok(fd) => fd,
Err(e) => return Err(e.into_compile_error()),
};
// Stringified name of the function:
let name_str = name_str.unwrap_or_else(|| LitStr::new(name.to_string().as_str(), name.span()));
// The number of arguments the function accepts:
let args_len = args.len();
// Generate token streams for some needed types:
let lazy = quote! {
std::sync::LazyLock
};
let core = quote! {
::serde_json_path_macros::serde_json_path_core::spec::functions
};
let res = quote! {
std::result::Result
};
// Generate code for checking each individual argument in a query at parse time:
let arg_checks = args.iter().enumerate().map(|(idx, arg)| {
let FnArgument { ident: _, ty } = arg;
quote! {
match a[#idx].as_type_kind() {
#res::Ok(tk) => {
if !tk.converts_to(#ty::json_path_type()) {
return #res::Err(#core::FunctionValidationError::MismatchTypeKind {
name: String::from(#name_str),
expected: #ty::json_path_type(),
received: tk,
position: #idx,
});
}
},
#res::Err(err) => return #res::Err(err)
}
}
});
// Generate the validator function used at parse time to validate a function declaration:
let validator = quote! {
static #validator_name: #core::Validator = #lazy::new(|| {
std::boxed::Box::new(|a: &[#core::FunctionExprArg]| {
if a.len() != #args_len {
return #res::Err(#core::FunctionValidationError::NumberOfArgsMismatch {
expected: a.len(),
received: #args_len,
});
}
#(#arg_checks)*
Ok(())
})
});
};
// Generate the code to declare each individual argument for evaluation, at query time:
let arg_declarations = args.iter().map(|arg| {
let FnArgument { ident, ty } = arg;
// validation should ensure unwrap is okay here:
quote! {
let #ident = #ty::try_from(v.pop_front().unwrap()).unwrap();
}
});
// Produce the argument name identifiers:
let arg_names = args.iter().map(|arg| {
let FnArgument { ident, ty: _ } = arg;
ident
});
// Generate the evaluator function used to evaluate a function at query time:
let evaluator = quote! {
fn #name #generics (#inputs) #ret #block
static #evaluator_name: #core::Evaluator = #lazy::new(|| {
std::boxed::Box::new(|mut v: std::collections::VecDeque<#core::JsonPathValue>| {
#(#arg_declarations)*
return #name(#(#arg_names,)*).into()
})
});
};
Ok(Expanded {
name_str,
validator,
validator_name,
evaluator,
evaluator_name,
result,
core,
})
}
|