Skip to content

Commit

Permalink
feat: make arg by ref & add arg support for mapper
Browse files Browse the repository at this point in the history
  • Loading branch information
AlseinX committed Nov 30, 2023
1 parent 8a810db commit dddef6a
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 97 deletions.
134 changes: 104 additions & 30 deletions macros/src/exprs/fn.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{
spanned::Spanned, token::Paren, Error, FnArg, Generics, Ident, ItemFn, Pat, PatIdent, PatType,
Result, ReturnType, Signature, Token, Type, TypeParam, TypePath, TypeReference, TypeTuple,
Visibility,
punctuated::Pair, spanned::Spanned, token::Paren, Error, FnArg, Generics, Ident, ItemFn, Pat,
PatIdent, PatType, Result, ReturnType, Signature, Token, Type, TypeParam, TypePath,
TypeReference, TypeTuple, Visibility,
};

pub fn handle_fn(f: &mut ItemFn, ty_id: &Ident) -> Result<TokenStream> {
Expand All @@ -30,40 +30,70 @@ pub fn handle_fn(f: &mut ItemFn, ty_id: &Ident) -> Result<TokenStream> {
}
}

macro_rules! extract_ref {
($t:expr) => {
if let Type::Reference(TypeReference {
mutability: None,
elem,
..
}) = $t
{
elem
} else {
return Err(Error::new($t.span(), "require an immutable reference"));
}
};
}

fn handle_map(
ItemFn {
attrs,
sig:
Signature {
unsafety,
fn_token,
generics:
Generics {
lt_token,
params,
gt_token,
where_clause,
},
inputs,
output: rtn_ty,
..
},
block,
..
}: &ItemFn,
attrs, sig, block, ..
}: &mut ItemFn,
ty_id: &Ident,
) -> Result<TokenStream> {
let span = sig.span().resolved_at(Span::mixed_site());
let Signature {
unsafety,
fn_token,
generics,
inputs,
output: rtn_ty,
..
} = sig;
let mut pt = Vec::with_capacity(inputs.len());
let mut pv = Vec::with_capacity(inputs.len());
let mut arg = None;

for input in inputs {
let FnArg::Typed(PatType { attrs, pat, ty, .. }) = input else {
while let Some(Pair::Punctuated(input, _) | Pair::End(input)) = inputs.pop() {
let FnArg::Typed(PatType {
mut attrs, pat, ty, ..
}) = input
else {
return Err(Error::new(
input.span(),
"self receievers are not supposed to be here",
));
};

if arg.is_none() {
if let Some(i) = attrs.iter().enumerate().find_map(|(i, a)| {
if let Ok(path) = a.meta.require_path_only() {
if path.is_ident("arg") {
Some(i)
} else {
None
}
} else {
None
}
}) {
attrs.remove(i);

arg = Some((pat, extract_ref!(*ty)));
continue;
}
}

if let Some(f) = attrs.first() {
return Err(Error::new(f.span(), "attributes are not supported here"));
}
Expand All @@ -72,6 +102,45 @@ fn handle_map(
pv.push(pat);
}

pt.reverse();
pv.reverse();

let (av, a) = if let Some((p, t)) = arg {
(p, *t)
} else {
let ident = Ident::new("__Arg", span);
generics.lt_token = Some(Token![<](span));
generics.params.push(syn::GenericParam::Type(TypeParam {
attrs: Default::default(),
ident: ident.clone(),
colon_token: None,
bounds: Default::default(),
eq_token: None,
default: None,
}));
generics.gt_token = Some(Token![>](span));
(
Box::new(Pat::Ident(PatIdent {
attrs: Default::default(),
by_ref: None,
mutability: None,
ident: Ident::new("_", span),
subpat: None,
})),
Type::Path(TypePath {
qself: None,
path: ident.into(),
}),
)
};

let Generics {
lt_token,
params,
gt_token,
where_clause,
} = generics;

let rtn_ty = if let ReturnType::Type(_, rtn_ty) = rtn_ty {
rtn_ty.as_ref().clone()
} else {
Expand All @@ -82,11 +151,11 @@ fn handle_map(
};

Ok(quote! {
impl #lt_token #params #gt_token ::xparse::ops::Mapper<(#(#pt,)*)> for #ty_id #where_clause {
impl #lt_token #params #gt_token ::xparse::ops::Mapper<(#(#pt,)*), #a> for #ty_id #where_clause {
type Output = #rtn_ty;
#(#attrs)*
#[inline]
#unsafety #fn_token map((#(#pv,)*): (#(#pt,)*)) -> Self::Output #block
#unsafety #fn_token map((#(#pv,)*): (#(#pt,)*), #av: &#a) -> Self::Output #block
}
})
}
Expand All @@ -105,7 +174,7 @@ fn handle_is(f: &mut ItemFn, ty_id: &Ident) -> Result<TokenStream> {
}

let a = if let Some(FnArg::Typed(PatType { ty: i, .. })) = inputs.iter().nth(1) {
i
extract_ref!(i.as_ref())
} else {
let ident = Ident::new("__Arg", span);
generics.lt_token = Some(Token![<](span));
Expand All @@ -128,9 +197,14 @@ fn handle_is(f: &mut ItemFn, ty_id: &Ident) -> Result<TokenStream> {
subpat: None,
})),
colon_token: Token![:](span),
ty: Box::new(Type::Path(TypePath {
qself: None,
path: ident.into(),
ty: Box::new(Type::Reference(TypeReference {
and_token: Token![&](span),
lifetime: None,
mutability: None,
elem: Box::new(Type::Path(TypePath {
qself: None,
path: ident.into(),
})),
})),
}));
let Some(FnArg::Typed(PatType { ty, .. })) = inputs.iter().nth(1) else {
Expand Down
24 changes: 10 additions & 14 deletions macros/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,7 @@ pub fn handle(
attrs: Default::default(),
ident: arg,
colon_token: Some(Token![:](span)),
bounds: Punctuated::from_iter([TypeParamBound::Trait(TraitBound {
paren_token: None,
modifier: TraitBoundModifier::None,
lifetimes: None,
path: Ident::new("Clone", span).into(),
})]),
bounds: Default::default(),
eq_token: Default::default(),
default: Default::default(),
}));
Expand Down Expand Up @@ -205,7 +200,7 @@ pub fn handle(

let f = quote! {
#[inline(always)]
fn parse<#s: ::xparse::Source<Item = #input>>(input: &mut #s, arg: #arg) -> Result<Self::Output> {
fn parse<#s: ::xparse::Source<Item = #input>>(input: &mut #s, arg: &#arg) -> Result<Self::Output> {
<#ty as ::xparse::parse::ParseImpl<#input, #arg>>::parse(input, arg)
}
};
Expand All @@ -214,7 +209,7 @@ pub fn handle(
let f = quote! {
#f
#[inline(always)]
async fn parse_async<S: ::xparse::AsyncSource<Item = #input>>(input: &mut S, arg: #arg) -> Result<Self::Output> {
async fn parse_async<S: ::xparse::AsyncSource<Item = #input>>(input: &mut S, arg: &#arg) -> Result<Self::Output> {
Box::pin(<#ty as ::xparse::parse::ParseImpl<#input, #arg>>::parse_async(input, arg)).await
}
};
Expand All @@ -226,24 +221,25 @@ pub fn handle(
type Output = #output;
#f
}

impl<#i, #a> ::xparse::ops::Predicate<#i, #a> for #ident
where
#ty: ::xparse::ops::Predicate<#i, #a>
{
#[inline(always)]
fn is(v: &#i, arg: #a) -> bool {
fn is(v: &#i, arg: &#a) -> bool {
<#ty as ::xparse::ops::Predicate<#i, #a>>::is(v, arg)
}
}

impl<#i> ::xparse::ops::Mapper<#i> for #ident
impl<#i, #a> ::xparse::ops::Mapper<#i, #a> for #ident
where
#ty: ::xparse::ops::Mapper<#i>
#ty: ::xparse::ops::Mapper<#i, #a>
{
type Output = <#ty as ::xparse::ops::Mapper<#i>>::Output;
type Output = <#ty as ::xparse::ops::Mapper<#i, #a>>::Output;
#[inline(always)]
fn map(v: #i) -> Self::Output {
<#ty as ::xparse::ops::Mapper<#i>>::map(v)
fn map(v: #i, a: &#a) -> Self::Output {
<#ty as ::xparse::ops::Mapper<#i, #a>>::map(v, a)
}
}
})
Expand Down
2 changes: 1 addition & 1 deletion src/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ type Object = Map<
Map<
And<Spaces, PRawString, Spaces, Discard<Colomn>, PValue>,
{
fn map(k: String, v: Value) -> (String, Value) {
fn map<K, V>(k: K, v: V) -> (K, V) {
(k, v)
}
},
Expand Down
Loading

0 comments on commit dddef6a

Please sign in to comment.