Skip to content
This repository was archived by the owner on Jun 2, 2020. It is now read-only.

Commit dbaeced

Browse files
committed
Implement support for binding attributes on parameters.
This commit implements support for binding attributes on parameters. For example: ```rust pub fn foo(#[binding(route = "foo") req: HttpRequest) { } ``` However, both `rustfmt` and `rls` (plus GitHub's source parser) do not yet properly handle attributes on parameters. The samples and documentation will be updated to specify the attributes on the parameters themselves once there is broader support of the feature. Closes #457.
1 parent a785912 commit dbaeced

File tree

2 files changed

+86
-37
lines changed

2 files changed

+86
-37
lines changed

azure-functions-codegen/src/func.rs

+83-34
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
mod invoker;
22
mod output_bindings;
33

4-
use crate::{attribute_args_from_name, parse_attribute_args};
4+
use crate::{create_name_attribute_arg, parse_attribute_args};
55
use azure_functions_shared::codegen::{
66
bindings::{
77
Binding, BindingFactory, INPUT_BINDINGS, INPUT_OUTPUT_BINDINGS, OUTPUT_BINDINGS, TRIGGERS,
@@ -335,47 +335,59 @@ fn get_input_binding_factory(
335335
}
336336
}
337337

338-
fn bind_input_type(
339-
pattern: &Pat,
340-
tp: &TypePath,
341-
mutability: Option<Mut>,
342-
has_trigger: bool,
343-
binding_args: &mut HashMap<String, (AttributeArgs, Span)>,
344-
) -> Binding {
345-
let factory = get_input_binding_factory(tp, mutability, has_trigger);
338+
fn drain_argument_binding_attribute(
339+
attrs: &mut Vec<Attribute>,
340+
name: &str,
341+
) -> Option<(AttributeArgs, Span)> {
342+
let mut result = None;
343+
for attr in attrs
344+
.iter()
345+
.filter(|a| last_segment_in_path(&a.path).ident == "binding")
346+
{
347+
if result.is_some() {
348+
macro_panic(
349+
attr.span(),
350+
"parameters cannot have more than one binding attribute",
351+
);
352+
}
346353

347-
match pattern {
348-
Pat::Ident(name) => {
349-
let name_str = name.ident.to_string();
350-
match binding_args.remove(&name_str) {
351-
Some(args) => (*factory)(args.0, args.1),
352-
None => {
353-
let name_span = name.ident.span();
354-
(*factory)(attribute_args_from_name(&name_str, name_span), name_span)
355-
}
354+
let mut args = parse_attribute_args(&attr);
355+
356+
iter_attribute_args(&args, |key, _| {
357+
if key == "name" {
358+
macro_panic(
359+
attr.span(),
360+
"parameter binding attributes cannot have a 'name' argument",
361+
);
356362
}
357-
}
358-
_ => macro_panic(pattern.span(), "bindings must have a named identifier"),
363+
true
364+
});
365+
366+
args.push(create_name_attribute_arg(name, attr.span()));
367+
368+
result = Some((args, attr.span()));
359369
}
370+
371+
attrs.retain(|a| last_segment_in_path(&a.path).ident != "binding");
372+
373+
result
360374
}
361375

362376
fn bind_argument(
363-
arg: &FnArg,
377+
arg: &mut FnArg,
364378
has_trigger: bool,
365379
binding_args: &mut HashMap<String, (AttributeArgs, Span)>,
366380
) -> Binding {
367-
match arg {
381+
let (pat, tp, mutability, attrs) = match arg {
368382
FnArg::Typed(arg) => match &*arg.ty {
369383
Type::Reference(tr) => match &*tr.elem {
370-
Type::Path(tp) => {
371-
bind_input_type(&*arg.pat, tp, tr.mutability, has_trigger, binding_args)
372-
}
384+
Type::Path(tp) => (&*arg.pat, tp, tr.mutability, &mut arg.attrs),
373385
_ => macro_panic(
374386
arg.ty.span(),
375387
"expected an Azure Functions trigger or input binding type",
376388
),
377389
},
378-
Type::Path(tp) => bind_input_type(&*arg.pat, tp, None, has_trigger, binding_args),
390+
Type::Path(tp) => (&*arg.pat, tp, None, &mut arg.attrs),
379391
_ => macro_panic(
380392
arg.ty.span(),
381393
"expected an Azure Functions trigger or input binding type",
@@ -384,6 +396,40 @@ fn bind_argument(
384396
FnArg::Receiver(_) => {
385397
macro_panic(arg.span(), "Azure Functions cannot have self parameters")
386398
}
399+
};
400+
401+
let factory = get_input_binding_factory(tp, mutability, has_trigger);
402+
403+
match pat {
404+
Pat::Ident(name) => {
405+
let name_str = name.ident.to_string();
406+
let binding_attr = drain_argument_binding_attribute(attrs, &name_str);
407+
408+
let binding_args = match binding_args.remove(&name_str) {
409+
Some(args) => {
410+
if let Some(binding_attr) = binding_attr {
411+
macro_panic(
412+
binding_attr.1,
413+
"parameter already has a binding attribute at the function level",
414+
);
415+
}
416+
args
417+
}
418+
None => binding_attr.unwrap_or_else(|| {
419+
let name_span = name.ident.span();
420+
(
421+
vec![create_name_attribute_arg(&name_str, name_span)],
422+
name_span,
423+
)
424+
}),
425+
};
426+
427+
(*factory)(binding_args.0, binding_args.1)
428+
}
429+
_ => macro_panic(
430+
pat.span(),
431+
"parameter bindings must have a named identifier",
432+
),
387433
}
388434
}
389435

@@ -409,7 +455,7 @@ fn bind_output_type(
409455
Some(args) => (*factory)(args.0, args.1),
410456
None => {
411457
let span = tp.span();
412-
(*factory)(attribute_args_from_name(name, span), span)
458+
(*factory)(vec![create_name_attribute_arg(name, span)], span)
413459
}
414460
}
415461
}
@@ -472,27 +518,30 @@ fn drain_binding_attributes(attrs: &mut Vec<Attribute>) -> HashMap<String, (Attr
472518
.iter()
473519
.filter(|a| last_segment_in_path(&a.path).ident == "binding")
474520
{
475-
let attr_span = attr.span();
476521
let args = parse_attribute_args(&attr);
477522
let mut name = None;
478-
let mut name_span = None;
479523

480524
iter_attribute_args(&args, |key, value| {
481525
if key != "name" {
482526
return true;
483527
}
484528

485529
name = Some(get_string_value("name", value));
486-
name_span = Some(key.span());
487530
false
488531
});
489532

490533
if name.is_none() {
491-
macro_panic(attr_span, "binding attributes must have a 'name' argument");
534+
macro_panic(
535+
attr.span(),
536+
"binding attributes must have a 'name' argument",
537+
);
492538
}
493539

494540
if map.insert(name.unwrap(), (args, attr.span())).is_some() {
495-
macro_panic(attr_span, "binding attributes must have a 'name' argument");
541+
macro_panic(
542+
attr.span(),
543+
"binding attributes must have a 'name' argument",
544+
);
496545
}
497546
}
498547

@@ -535,8 +584,8 @@ pub fn func_impl(
535584
let mut binding_args = drain_binding_attributes(&mut target.attrs);
536585
let mut names = HashSet::new();
537586
let mut has_trigger = false;
538-
for arg in &target.sig.inputs {
539-
let binding = bind_argument(&arg, has_trigger, &mut binding_args);
587+
for arg in target.sig.inputs.iter_mut() {
588+
let binding = bind_argument(arg, has_trigger, &mut binding_args);
540589
has_trigger |= binding.is_trigger();
541590

542591
if let Some(name) = binding.name() {

azure-functions-codegen/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ fn parse_attribute_args(attr: &Attribute) -> AttributeArgs {
3131
.unwrap()
3232
}
3333

34-
fn attribute_args_from_name(name: &str, span: Span) -> AttributeArgs {
35-
vec![NestedMeta::Meta(Meta::NameValue(MetaNameValue {
34+
fn create_name_attribute_arg(name: &str, span: Span) -> NestedMeta {
35+
NestedMeta::Meta(Meta::NameValue(MetaNameValue {
3636
path: Ident::new("name", span).into(),
3737
eq_token: Eq { spans: [span] },
3838
lit: Lit::Str(LitStr::new(name, span)),
39-
}))]
39+
}))
4040
}
4141

4242
/// Implements the `export!` macro.

0 commit comments

Comments
 (0)