use std::collections::VecDeque; use proc_macro2::{Ident, TokenStream}; use quote::quote; use syn::{ punctuated::Punctuated, spanned::Spanned, token::Comma, Error, FnArg, Generics, Pat, PatType, Path, Result, ReturnType, Signature, Type, }; pub struct Components { pub name: Ident, pub generics: Generics, pub validator_name: Ident, pub evaluator_name: Ident, pub result: TokenStream, pub ret: ReturnType, pub inputs: Punctuated, pub args: VecDeque, } pub struct FnArgument { pub ident: Ident, pub ty: TokenStream, } fn extract_pat_ident(pat: &Pat) -> Option { if let Pat::Ident(ref pat_ident) = pat { Some(pat_ident.ident.to_owned()) } else { None } } fn extract_type_path(ty: &Type) -> Option<&Path> { match ty { Type::Path(ref typepath) if typepath.qself.is_none() => Some(&typepath.path), Type::Reference(ref typeref) => match *typeref.elem { Type::Path(ref typepath) if typepath.qself.is_none() => Some(&typepath.path), _ => None, }, _ => None, } } fn extract_json_path_type(p: &Path) -> Result { let p_seg = p .segments .last() .ok_or_else(|| Error::new(p.span(), "expected a type identifier"))?; let ts = match p_seg.ident.to_string().as_str() { "NodesType" => quote! { ::serde_json_path_macros::serde_json_path_core::spec::functions::NodesType }, "ValueType" => quote! { ::serde_json_path_macros::serde_json_path_core::spec::functions::ValueType }, "LogicalType" => quote! { ::serde_json_path_macros::serde_json_path_core::spec::functions::LogicalType }, other => { return Err(Error::new( p_seg.ident.span(), format!( "expected 'NodesType', 'ValueType', or 'LogicalType', got '{}'", other, ), )) } }; Ok(ts) } pub fn extract_components(input: Signature) -> Result { let name = input.ident.clone(); let generics = input.generics.clone(); let inputs = input.inputs.clone(); let ret = input.output.clone(); let result = match &ret { ReturnType::Default => { return Err(Error::new( input.span(), "function signature expected to have return type", )) } ReturnType::Type(_, ty) => { if let Some(path) = extract_type_path(ty.as_ref()) { extract_json_path_type(path)? } else { return Err(Error::new( ty.span(), "return type can only be one of the serde_json_path types: NodesType, \ ValueType, or LogicalType", )); } } }; let args: Result> = inputs .iter() .map(|i| match i { FnArg::Receiver(_) => Err(Error::new( inputs.span(), "receiver arguments like self, &self, or &mut self are not supported", )), FnArg::Typed(PatType { attrs: _, pat, colon_token: _, ty, }) => { let ident = if let Some(id) = extract_pat_ident(pat) { id } else { return Err(Error::new( pat.span(), "expected identifier in function argument", )); }; let ty = if let Some(path) = extract_type_path(ty) { extract_json_path_type(path)? } else { return Err(Error::new( ty.span(), "argument type can only be one of the serde_json_path types: NodesType, \ ValueType, or LogicalType", )); }; Ok(FnArgument { ident, ty }) } }) .collect(); let args = args?; let validator_name = Ident::new( format!("{name}_validator").to_uppercase().as_str(), name.span(), ); let evaluator_name = Ident::new( format!("{name}_evaluator").to_uppercase().as_str(), name.span(), ); Ok(Components { name, generics, result, inputs, ret, args, validator_name, evaluator_name, }) }