2017-05-20 06:14:59 +00:00
|
|
|
// Copyright (c) 2017-present PyO3 Project and Contributors
|
|
|
|
|
|
|
|
use syn;
|
2017-06-14 05:37:26 +00:00
|
|
|
use quote::{Tokens, Ident};
|
|
|
|
|
|
|
|
use args::{Argument, parse_arguments};
|
2017-05-20 06:14:59 +00:00
|
|
|
use utils::for_err_msg;
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct FnArg<'a> {
|
|
|
|
pub name: &'a syn::Ident,
|
|
|
|
pub mode: &'a syn::BindingMode,
|
|
|
|
pub ty: &'a syn::Ty,
|
|
|
|
pub optional: Option<&'a syn::Ty>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, PartialEq, Debug)]
|
|
|
|
pub enum FnType {
|
|
|
|
Getter(Option<String>),
|
|
|
|
Setter(Option<String>),
|
|
|
|
Fn,
|
|
|
|
FnNew,
|
|
|
|
FnCall,
|
2017-06-08 18:29:40 +00:00
|
|
|
FnClass,
|
|
|
|
FnStatic,
|
2017-05-20 06:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct FnSpec<'a> {
|
|
|
|
pub tp: FnType,
|
2017-06-14 05:37:26 +00:00
|
|
|
pub attrs: Vec<Argument>,
|
2017-05-20 06:14:59 +00:00
|
|
|
pub args: Vec<FnArg<'a>>,
|
2017-05-26 23:51:33 +00:00
|
|
|
pub output: syn::Ty,
|
2017-05-20 06:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FnSpec<'a> {
|
|
|
|
|
|
|
|
pub fn parse(name: &'a syn::Ident,
|
|
|
|
sig: &'a syn::MethodSig,
|
|
|
|
meth_attrs: &'a mut Vec<syn::Attribute>) -> FnSpec<'a> {
|
|
|
|
let (fn_type, fn_attrs) = parse_attributes(meth_attrs);
|
|
|
|
|
2017-06-08 18:29:40 +00:00
|
|
|
let mut has_self = false;
|
2017-05-26 23:51:33 +00:00
|
|
|
let mut py = false;
|
2017-05-20 06:14:59 +00:00
|
|
|
let mut arguments = Vec::new();
|
|
|
|
|
2017-06-08 18:29:40 +00:00
|
|
|
for input in sig.decl.inputs.iter() {
|
2017-05-20 06:14:59 +00:00
|
|
|
match input {
|
|
|
|
&syn::FnArg::SelfRef(_, _) => {
|
2017-06-08 18:29:40 +00:00
|
|
|
has_self = true;
|
2017-05-20 06:14:59 +00:00
|
|
|
},
|
|
|
|
&syn::FnArg::SelfValue(_) => {
|
2017-06-08 18:29:40 +00:00
|
|
|
has_self = true;
|
2017-05-20 06:14:59 +00:00
|
|
|
}
|
|
|
|
&syn::FnArg::Captured(ref pat, ref ty) => {
|
2017-06-08 18:29:40 +00:00
|
|
|
// skip first argument (cls)
|
|
|
|
if (fn_type == FnType::FnClass || fn_type == FnType::FnNew) && !has_self {
|
|
|
|
has_self = true;
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2017-05-20 06:14:59 +00:00
|
|
|
let (mode, ident) = match pat {
|
|
|
|
&syn::Pat::Ident(ref mode, ref ident, _) =>
|
|
|
|
(mode, ident),
|
|
|
|
_ =>
|
|
|
|
panic!("unsupported argument: {:?}", pat),
|
|
|
|
};
|
2017-06-08 18:29:40 +00:00
|
|
|
|
|
|
|
if !py {
|
|
|
|
match ty {
|
|
|
|
&syn::Ty::Path(_, ref path) =>
|
|
|
|
if let Some(segment) = path.segments.last() {
|
|
|
|
if segment.ident.as_ref() == "Python" {
|
|
|
|
py = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => (),
|
|
|
|
}
|
2017-05-20 06:14:59 +00:00
|
|
|
}
|
2017-06-08 18:29:40 +00:00
|
|
|
|
|
|
|
let opt = check_arg_ty_and_optional(name, ty);
|
|
|
|
arguments.push(FnArg{name: ident, mode: mode, ty: ty, optional: opt});
|
2017-05-20 06:14:59 +00:00
|
|
|
}
|
|
|
|
&syn::FnArg::Ignored(_) =>
|
|
|
|
panic!("ignored argument: {:?}", name),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-26 23:51:33 +00:00
|
|
|
let ty = match sig.decl.output {
|
|
|
|
syn::FunctionRetTy::Default => syn::Ty::Infer,
|
|
|
|
syn::FunctionRetTy::Ty(ref ty) => ty.clone()
|
|
|
|
};
|
|
|
|
|
2017-05-20 06:14:59 +00:00
|
|
|
FnSpec {
|
|
|
|
tp: fn_type,
|
|
|
|
attrs: fn_attrs,
|
2017-05-26 23:51:33 +00:00
|
|
|
args: arguments,
|
|
|
|
output: ty,
|
2017-05-20 06:14:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_args(&self, name: &syn::Ident) -> bool {
|
|
|
|
for s in self.attrs.iter() {
|
|
|
|
match *s {
|
2017-06-14 05:37:26 +00:00
|
|
|
Argument::VarArgs(ref ident) =>
|
|
|
|
return name.as_ref() == ident.as_str(),
|
2017-05-20 06:14:59 +00:00
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn accept_args(&self) -> bool {
|
|
|
|
for s in self.attrs.iter() {
|
|
|
|
match *s {
|
2017-06-14 05:37:26 +00:00
|
|
|
Argument::VarArgs(_) => return true,
|
|
|
|
Argument::VarArgsSeparator => return true,
|
2017-05-20 06:14:59 +00:00
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_kwargs(&self, name: &syn::Ident) -> bool {
|
|
|
|
for s in self.attrs.iter() {
|
|
|
|
match *s {
|
2017-06-14 05:37:26 +00:00
|
|
|
Argument::KeywordArgs(ref ident) =>
|
|
|
|
return name.as_ref() == ident.as_str(),
|
2017-05-20 06:14:59 +00:00
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn accept_kwargs(&self) -> bool {
|
|
|
|
for s in self.attrs.iter() {
|
|
|
|
match *s {
|
2017-06-14 05:37:26 +00:00
|
|
|
Argument::KeywordArgs(_) => return true,
|
2017-05-20 06:14:59 +00:00
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn default_value(&self, name: &syn::Ident) -> Option<Tokens> {
|
|
|
|
for s in self.attrs.iter() {
|
|
|
|
match *s {
|
2017-06-14 05:37:26 +00:00
|
|
|
Argument::Arg(ref ident, ref opt) => {
|
|
|
|
if ident.as_str() == name.as_ref() {
|
|
|
|
if let &Some(ref val) = opt {
|
|
|
|
let i = Ident::from(val.as_str());
|
|
|
|
return Some(quote!(#i))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Argument::Kwarg(ref ident, ref opt) => {
|
|
|
|
if ident.as_str() == name.as_ref() {
|
|
|
|
let i = Ident::from(opt.as_str());
|
|
|
|
return Some(quote!(#i))
|
2017-05-20 06:14:59 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
2017-06-14 05:37:26 +00:00
|
|
|
|
|
|
|
pub fn is_kw_only(&self, name: &syn::Ident) -> bool {
|
|
|
|
for s in self.attrs.iter() {
|
|
|
|
match *s {
|
|
|
|
Argument::Kwarg(ref ident, _) => {
|
|
|
|
if ident.as_str() == name.as_ref() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
false
|
|
|
|
}
|
2017-05-20 06:14:59 +00:00
|
|
|
}
|
|
|
|
|
2017-06-14 21:08:30 +00:00
|
|
|
pub fn check_arg_ty_and_optional<'a>(name: &'a syn::Ident, ty: &'a syn::Ty)
|
|
|
|
-> Option<&'a syn::Ty>
|
|
|
|
{
|
2017-05-20 06:14:59 +00:00
|
|
|
match ty {
|
2017-05-25 05:43:07 +00:00
|
|
|
&syn::Ty::Path(_, ref path) => {
|
|
|
|
//if let &Some(ref qs) = qs {
|
|
|
|
// panic!("explicit Self type in a 'qualified path' is not supported: {:?} - {:?}",
|
|
|
|
// name, qs);
|
|
|
|
//}
|
2017-05-20 06:14:59 +00:00
|
|
|
|
|
|
|
if let Some(segment) = path.segments.last() {
|
|
|
|
match segment.ident.as_ref() {
|
|
|
|
"Option" => {
|
|
|
|
match segment.parameters {
|
|
|
|
syn::PathParameters::AngleBracketed(ref params) => {
|
|
|
|
if params.types.len() != 1 {
|
|
|
|
panic!("argument type is not supported by python method: {:?} ({:?}) {:?}",
|
|
|
|
for_err_msg(name),
|
|
|
|
for_err_msg(ty),
|
|
|
|
for_err_msg(path));
|
|
|
|
}
|
|
|
|
Some(¶ms.types[0])
|
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
panic!("argument type is not supported by python method: {:?} ({:?}) {:?}",
|
|
|
|
for_err_msg(name),
|
|
|
|
for_err_msg(ty),
|
|
|
|
for_err_msg(path));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
None
|
|
|
|
//panic!("argument type is not supported by python method: {:?} ({:?})",
|
|
|
|
//for_err_msg(name),
|
|
|
|
//for_err_msg(ty));
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-14 05:37:26 +00:00
|
|
|
fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> (FnType, Vec<Argument>) {
|
2017-05-20 06:14:59 +00:00
|
|
|
let mut new_attrs = Vec::new();
|
|
|
|
let mut spec = Vec::new();
|
|
|
|
let mut res: Option<FnType> = None;
|
|
|
|
|
|
|
|
for attr in attrs.iter() {
|
|
|
|
match attr.value {
|
|
|
|
syn::MetaItem::Word(ref name) => {
|
|
|
|
match name.as_ref() {
|
2017-05-20 17:40:33 +00:00
|
|
|
"new" | "__new__" => {
|
2017-05-20 06:14:59 +00:00
|
|
|
res = Some(FnType::FnNew)
|
|
|
|
},
|
2017-05-20 17:40:33 +00:00
|
|
|
"call" | "__call__" => {
|
2017-05-20 06:14:59 +00:00
|
|
|
res = Some(FnType::FnCall)
|
|
|
|
},
|
2017-06-08 18:29:40 +00:00
|
|
|
"classmethod" => {
|
|
|
|
res = Some(FnType::FnClass)
|
|
|
|
},
|
|
|
|
"staticmethod" => {
|
|
|
|
res = Some(FnType::FnStatic)
|
|
|
|
},
|
2017-05-20 06:14:59 +00:00
|
|
|
"setter" | "getter" => {
|
|
|
|
if attr.style == syn::AttrStyle::Inner {
|
|
|
|
panic!("Inner style attribute is not
|
|
|
|
supported for setter and getter");
|
|
|
|
}
|
|
|
|
if res != None {
|
|
|
|
panic!("setter/getter attribute can not be used mutiple times");
|
|
|
|
}
|
|
|
|
if name.as_ref() == "setter" {
|
|
|
|
res = Some(FnType::Setter(None))
|
|
|
|
} else {
|
|
|
|
res = Some(FnType::Getter(None))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
new_attrs.push(attr.clone())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
syn::MetaItem::List(ref name, ref meta) => {
|
|
|
|
match name.as_ref() {
|
|
|
|
"new" => {
|
|
|
|
res = Some(FnType::FnNew)
|
|
|
|
},
|
|
|
|
"call" => {
|
|
|
|
res = Some(FnType::FnCall)
|
|
|
|
},
|
|
|
|
"setter" | "getter" => {
|
|
|
|
if attr.style == syn::AttrStyle::Inner {
|
|
|
|
panic!("Inner style attribute is not
|
|
|
|
supported for setter and getter");
|
|
|
|
}
|
|
|
|
if res != None {
|
|
|
|
panic!("setter/getter attribute can not be used mutiple times");
|
|
|
|
}
|
|
|
|
if meta.len() != 1 {
|
|
|
|
panic!("setter/getter requires one value");
|
|
|
|
}
|
|
|
|
match *meta.first().unwrap() {
|
|
|
|
syn::NestedMetaItem::MetaItem(syn::MetaItem::Word(ref w)) => {
|
|
|
|
if name.as_ref() == "setter" {
|
|
|
|
res = Some(FnType::Setter(Some(w.to_string())))
|
|
|
|
} else {
|
|
|
|
res = Some(FnType::Getter(Some(w.to_string())))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
syn::NestedMetaItem::Literal(ref lit) => {
|
|
|
|
match *lit {
|
|
|
|
syn::Lit::Str(ref s, syn::StrStyle::Cooked) => {
|
|
|
|
if name.as_ref() == "setter" {
|
|
|
|
res = Some(FnType::Setter(Some(s.clone())))
|
|
|
|
} else {
|
|
|
|
res = Some(FnType::Getter(Some(s.clone())))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
panic!("setter/getter attribute requires str value");
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
println!("cannot parse {:?} attribute: {:?}", name, meta);
|
|
|
|
},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"args" => {
|
|
|
|
spec.extend(parse_args(meta))
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
new_attrs.push(attr.clone())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
syn::MetaItem::NameValue(_, _) => {
|
|
|
|
new_attrs.push(attr.clone())
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
attrs.clear();
|
|
|
|
attrs.extend(new_attrs);
|
|
|
|
|
|
|
|
match res {
|
|
|
|
Some(tp) => (tp, spec),
|
|
|
|
None => (FnType::Fn, spec),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// parse: #[args(args="args", kw="kwargs")]
|
2017-06-14 05:37:26 +00:00
|
|
|
fn parse_args(items: &Vec<syn::NestedMetaItem>) -> Vec<Argument> {
|
2017-05-20 06:14:59 +00:00
|
|
|
let mut spec = Vec::new();
|
|
|
|
|
|
|
|
for item in items.iter() {
|
|
|
|
match item {
|
|
|
|
&syn::NestedMetaItem::MetaItem(syn::MetaItem::NameValue(ref ident, ref name)) => {
|
|
|
|
match *name {
|
|
|
|
syn::Lit::Str(ref name, _) => match ident.as_ref() {
|
|
|
|
"args" =>
|
2017-06-14 05:37:26 +00:00
|
|
|
spec.push(Argument::VarArgs(name.clone())),
|
2017-05-20 06:14:59 +00:00
|
|
|
"kw" =>
|
2017-06-14 05:37:26 +00:00
|
|
|
spec.push(Argument::KeywordArgs(name.clone())),
|
2017-05-20 06:14:59 +00:00
|
|
|
_ => (),
|
|
|
|
},
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
},
|
2017-06-14 05:37:26 +00:00
|
|
|
&syn::NestedMetaItem::Literal(syn::Lit::Str(ref args, _)) => {
|
|
|
|
for item in parse_arguments(args.as_ref()) {
|
|
|
|
spec.push(item);
|
|
|
|
}
|
|
|
|
},
|
2017-05-20 06:14:59 +00:00
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
spec
|
|
|
|
}
|