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

Commit

Permalink
Merge pull request #465 from peterhuene/binding-attributes-on-parameters
Browse files Browse the repository at this point in the history
Implement support for binding attributes on parameters.
  • Loading branch information
peterhuene authored Nov 14, 2019
2 parents a785912 + dbaeced commit a515f94
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 37 deletions.
117 changes: 83 additions & 34 deletions azure-functions-codegen/src/func.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
mod invoker;
mod output_bindings;

use crate::{attribute_args_from_name, parse_attribute_args};
use crate::{create_name_attribute_arg, parse_attribute_args};
use azure_functions_shared::codegen::{
bindings::{
Binding, BindingFactory, INPUT_BINDINGS, INPUT_OUTPUT_BINDINGS, OUTPUT_BINDINGS, TRIGGERS,
Expand Down Expand Up @@ -335,47 +335,59 @@ fn get_input_binding_factory(
}
}

fn bind_input_type(
pattern: &Pat,
tp: &TypePath,
mutability: Option<Mut>,
has_trigger: bool,
binding_args: &mut HashMap<String, (AttributeArgs, Span)>,
) -> Binding {
let factory = get_input_binding_factory(tp, mutability, has_trigger);
fn drain_argument_binding_attribute(
attrs: &mut Vec<Attribute>,
name: &str,
) -> Option<(AttributeArgs, Span)> {
let mut result = None;
for attr in attrs
.iter()
.filter(|a| last_segment_in_path(&a.path).ident == "binding")
{
if result.is_some() {
macro_panic(
attr.span(),
"parameters cannot have more than one binding attribute",
);
}

match pattern {
Pat::Ident(name) => {
let name_str = name.ident.to_string();
match binding_args.remove(&name_str) {
Some(args) => (*factory)(args.0, args.1),
None => {
let name_span = name.ident.span();
(*factory)(attribute_args_from_name(&name_str, name_span), name_span)
}
let mut args = parse_attribute_args(&attr);

iter_attribute_args(&args, |key, _| {
if key == "name" {
macro_panic(
attr.span(),
"parameter binding attributes cannot have a 'name' argument",
);
}
}
_ => macro_panic(pattern.span(), "bindings must have a named identifier"),
true
});

args.push(create_name_attribute_arg(name, attr.span()));

result = Some((args, attr.span()));
}

attrs.retain(|a| last_segment_in_path(&a.path).ident != "binding");

result
}

fn bind_argument(
arg: &FnArg,
arg: &mut FnArg,
has_trigger: bool,
binding_args: &mut HashMap<String, (AttributeArgs, Span)>,
) -> Binding {
match arg {
let (pat, tp, mutability, attrs) = match arg {
FnArg::Typed(arg) => match &*arg.ty {
Type::Reference(tr) => match &*tr.elem {
Type::Path(tp) => {
bind_input_type(&*arg.pat, tp, tr.mutability, has_trigger, binding_args)
}
Type::Path(tp) => (&*arg.pat, tp, tr.mutability, &mut arg.attrs),
_ => macro_panic(
arg.ty.span(),
"expected an Azure Functions trigger or input binding type",
),
},
Type::Path(tp) => bind_input_type(&*arg.pat, tp, None, has_trigger, binding_args),
Type::Path(tp) => (&*arg.pat, tp, None, &mut arg.attrs),
_ => macro_panic(
arg.ty.span(),
"expected an Azure Functions trigger or input binding type",
Expand All @@ -384,6 +396,40 @@ fn bind_argument(
FnArg::Receiver(_) => {
macro_panic(arg.span(), "Azure Functions cannot have self parameters")
}
};

let factory = get_input_binding_factory(tp, mutability, has_trigger);

match pat {
Pat::Ident(name) => {
let name_str = name.ident.to_string();
let binding_attr = drain_argument_binding_attribute(attrs, &name_str);

let binding_args = match binding_args.remove(&name_str) {
Some(args) => {
if let Some(binding_attr) = binding_attr {
macro_panic(
binding_attr.1,
"parameter already has a binding attribute at the function level",
);
}
args
}
None => binding_attr.unwrap_or_else(|| {
let name_span = name.ident.span();
(
vec![create_name_attribute_arg(&name_str, name_span)],
name_span,
)
}),
};

(*factory)(binding_args.0, binding_args.1)
}
_ => macro_panic(
pat.span(),
"parameter bindings must have a named identifier",
),
}
}

Expand All @@ -409,7 +455,7 @@ fn bind_output_type(
Some(args) => (*factory)(args.0, args.1),
None => {
let span = tp.span();
(*factory)(attribute_args_from_name(name, span), span)
(*factory)(vec![create_name_attribute_arg(name, span)], span)
}
}
}
Expand Down Expand Up @@ -472,27 +518,30 @@ fn drain_binding_attributes(attrs: &mut Vec<Attribute>) -> HashMap<String, (Attr
.iter()
.filter(|a| last_segment_in_path(&a.path).ident == "binding")
{
let attr_span = attr.span();
let args = parse_attribute_args(&attr);
let mut name = None;
let mut name_span = None;

iter_attribute_args(&args, |key, value| {
if key != "name" {
return true;
}

name = Some(get_string_value("name", value));
name_span = Some(key.span());
false
});

if name.is_none() {
macro_panic(attr_span, "binding attributes must have a 'name' argument");
macro_panic(
attr.span(),
"binding attributes must have a 'name' argument",
);
}

if map.insert(name.unwrap(), (args, attr.span())).is_some() {
macro_panic(attr_span, "binding attributes must have a 'name' argument");
macro_panic(
attr.span(),
"binding attributes must have a 'name' argument",
);
}
}

Expand Down Expand Up @@ -535,8 +584,8 @@ pub fn func_impl(
let mut binding_args = drain_binding_attributes(&mut target.attrs);
let mut names = HashSet::new();
let mut has_trigger = false;
for arg in &target.sig.inputs {
let binding = bind_argument(&arg, has_trigger, &mut binding_args);
for arg in target.sig.inputs.iter_mut() {
let binding = bind_argument(arg, has_trigger, &mut binding_args);
has_trigger |= binding.is_trigger();

if let Some(name) = binding.name() {
Expand Down
6 changes: 3 additions & 3 deletions azure-functions-codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ fn parse_attribute_args(attr: &Attribute) -> AttributeArgs {
.unwrap()
}

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

/// Implements the `export!` macro.
Expand Down

0 comments on commit a515f94

Please sign in to comment.