pyo3/pyo3-derive-backend/src/args.rs
2018-05-13 21:24:40 +02:00

204 lines
8.4 KiB
Rust

// Copyright (c) 2017-present PyO3 Project and Contributors
use syn;
#[derive(Debug, Clone, PartialEq)]
pub enum Argument {
VarArgsSeparator,
VarArgs(String),
KeywordArgs(String),
Arg(String, Option<String>),
Kwarg(String, String),
}
pub fn parse_arguments(items: &[syn::NestedMeta]) -> Vec<Argument> {
let mut arguments = Vec::new();
let mut has_kw = false;
let mut has_varargs = false;
let mut has_kwargs = false;
let args_str = quote! {
#(#items),*
}.to_string();
for item in items.iter() {
match item {
&syn::NestedMeta::Meta(syn::Meta::Word(ref ident)) => {
// arguments in form #[args(somename)]
if has_kwargs {
println!("syntax error, keyword arguments is defined: {:?}", args_str);
return Vec::new();
}
if has_kw {
println!("syntax error, argument is not allowed after keyword argument: {:?}",
args_str);
return Vec::new()
}
arguments.push(Argument::Arg(ident.as_ref().to_owned(), None))
}
&syn::NestedMeta::Meta(syn::Meta::NameValue(ref nv)) => {
let name = nv.ident.as_ref().to_owned();
match nv.lit {
syn::Lit::Str(ref litstr) => {
if litstr.value() == "*" { // #[args(args="*")]
if has_kwargs {
println!("* - syntax error, keyword arguments is defined: {:?}",
args_str);
return Vec::new()
}
if has_varargs {
println!("*(var args) is defined: {:?}", args_str);
return Vec::new()
}
has_varargs = true;
arguments.push(Argument::VarArgs(name));
} else if litstr.value() == "**" { // #[args(kwargs="**")]
if has_kwargs {
println!("arguments already define ** (kw args): {:?}",
args_str);
return Vec::new()
}
has_kwargs = true;
arguments.push(Argument::KeywordArgs(name));
} else {
if has_varargs {
arguments.push(Argument::Kwarg(name, litstr.value().clone()))
} else {
if has_kwargs {
println!("syntax error, keyword arguments is defined: {:?}",
args_str);
return Vec::new()
}
has_kw = true;
arguments.push(Argument::Arg(name, Some(litstr.value().clone())))
}
}
}
syn::Lit::Int(ref litint) => {
if has_varargs {
arguments.push(Argument::Kwarg(name, format!("{}", litint.value())));
} else {
if has_kwargs {
println!("syntax error, keyword arguments is defined: {:?}",
args_str);
return Vec::new()
}
has_kw = true;
arguments.push(Argument::Arg(name, Some(format!("{}", litint.value()))));
}
}
syn::Lit::Bool(ref litb) => {
if has_varargs {
arguments.push(Argument::Kwarg(name, format!("{}", litb.value)));
} else {
if has_kwargs {
println!("syntax error, keyword arguments is defined: {:?}",
args_str);
return Vec::new()
}
has_kw = true;
arguments.push(Argument::Arg(name, Some(format!("{}", litb.value))));
}
}
_ => {
println!("Only string literal is supported, got: {:?}", nv.lit);
return Vec::new()
}
}
}
&syn::NestedMeta::Literal(ref lit) => {
match lit {
&syn::Lit::Str(ref lits) => {
// #[args("*")]
if lits.value() == "*" {
if has_kwargs {
println!(
"syntax error, keyword arguments is defined: {:?}",
args_str);
return Vec::new()
}
if has_varargs {
println!(
"arguments already define * (var args): {:?}",
args_str);
return Vec::new()
}
has_varargs = true;
arguments.push(Argument::VarArgsSeparator);
} else {
println!("Unknown string literal, got: {:?} args: {:?}",
lits.value(), args_str);
return Vec::new()
}
}
_ => {
println!("Only string literal is supported, got: {:?} args: {:?}",
lit, args_str);
return Vec::new()
}
}
}
_ => {
println!("Unknown argument, got: {:?} args: {:?}", item, args_str);
return Vec::new()
}
}
}
arguments
}
#[cfg(test)]
mod test {
use syn;
use args::{Argument, parse_arguments};
fn items(s: &'static str) -> Vec<syn::NestedMeta> {
let i = syn::parse_outer_attr(s).unwrap();
match i.value {
syn::Meta::List(_, items) => {
items
}
_ => unreachable!()
}
}
#[test]
fn test_errs() {
assert!(parse_arguments(&items("#[args(test=\"1\", test2)]")).is_empty());
assert!(parse_arguments(&items("#[args(test=1, \"*\", args=\"*\")]")).is_empty());
assert!(parse_arguments(&items("#[args(test=1, kwargs=\"**\", args=\"*\")]")).is_empty());
assert!(parse_arguments(&items("#[args(test=1, kwargs=\"**\", args)]")).is_empty());
}
#[test]
fn test_simple_args() {
let args = parse_arguments(&items("#[args(test1, test2, test3=\"None\")]"));
assert!(args == vec![Argument::Arg("test1".to_owned(), None),
Argument::Arg("test2".to_owned(), None),
Argument::Arg("test3".to_owned(), Some("None".to_owned()))]);
}
#[test]
fn test_varargs() {
let args = parse_arguments(
&items("#[args(test1, test2=\"None\", \"*\", test3=\"None\")]"));
assert!(args == vec![Argument::Arg("test1".to_owned(), None),
Argument::Arg("test2".to_owned(), Some("None".to_owned())),
Argument::VarArgsSeparator,
Argument::Kwarg("test3".to_owned(), "None".to_owned())]);
}
#[test]
fn test_all() {
let args = parse_arguments(
&items("#[args(test1, test2=\"None\", args=\"*\", test3=\"None\", kwargs=\"**\")]"));
assert!(args == vec![Argument::Arg("test1".to_owned(), None),
Argument::Arg("test2".to_owned(), Some("None".to_owned())),
Argument::VarArgs("args".to_owned()),
Argument::Kwarg("test3".to_owned(), "None".to_owned()),
Argument::KeywordArgs("kwargs".to_owned())]);
}
}