Skip to content

Commit f11f04d

Browse files
committed
Split rustpython_derive into multiple files
1 parent d9efe4e commit f11f04d

File tree

4 files changed

+390
-377
lines changed

4 files changed

+390
-377
lines changed

derive/src/from_args.rs

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
use super::rustpython_path_derive;
2+
use proc_macro2::TokenStream as TokenStream2;
3+
use quote::quote;
4+
use syn::{Attribute, Data, DeriveInput, Expr, Field, Fields, Ident, Lit, Meta, NestedMeta};
5+
6+
/// The kind of the python parameter, this corresponds to the value of Parameter.kind
7+
/// (https://docs.python.org/3/library/inspect.html#inspect.Parameter.kind)
8+
enum ParameterKind {
9+
PositionalOnly,
10+
PositionalOrKeyword,
11+
KeywordOnly,
12+
}
13+
14+
impl ParameterKind {
15+
fn from_ident(ident: &Ident) -> ParameterKind {
16+
if ident == "positional_only" {
17+
ParameterKind::PositionalOnly
18+
} else if ident == "positional_or_keyword" {
19+
ParameterKind::PositionalOrKeyword
20+
} else if ident == "keyword_only" {
21+
ParameterKind::KeywordOnly
22+
} else {
23+
panic!("Unrecognised attribute")
24+
}
25+
}
26+
}
27+
28+
struct ArgAttribute {
29+
kind: ParameterKind,
30+
default: Option<Expr>,
31+
optional: bool,
32+
}
33+
34+
impl ArgAttribute {
35+
fn from_attribute(attr: &Attribute) -> Option<ArgAttribute> {
36+
if !attr.path.is_ident("pyarg") {
37+
return None;
38+
}
39+
40+
match attr.parse_meta().unwrap() {
41+
Meta::List(list) => {
42+
let mut iter = list.nested.iter();
43+
let first_arg = iter.next().expect("at least one argument in pyarg list");
44+
let kind = match first_arg {
45+
NestedMeta::Meta(Meta::Word(ident)) => ParameterKind::from_ident(ident),
46+
_ => panic!("Bad syntax for first pyarg attribute argument"),
47+
};
48+
49+
let mut attribute = ArgAttribute {
50+
kind,
51+
default: None,
52+
optional: false,
53+
};
54+
55+
while let Some(arg) = iter.next() {
56+
attribute.parse_argument(arg);
57+
}
58+
59+
assert!(
60+
attribute.default.is_none() || !attribute.optional,
61+
"Can't set both a default value and optional"
62+
);
63+
64+
Some(attribute)
65+
}
66+
_ => panic!("Bad syntax for pyarg attribute"),
67+
}
68+
}
69+
70+
fn parse_argument(&mut self, arg: &NestedMeta) {
71+
match arg {
72+
NestedMeta::Meta(Meta::Word(ident)) => {
73+
if ident == "default" {
74+
assert!(self.default.is_none(), "Default already set");
75+
let expr = syn::parse_str::<Expr>("Default::default()").unwrap();
76+
self.default = Some(expr);
77+
} else if ident == "optional" {
78+
self.optional = true;
79+
} else {
80+
panic!("Unrecognised pyarg attribute '{}'", ident);
81+
}
82+
}
83+
NestedMeta::Meta(Meta::NameValue(name_value)) => {
84+
if name_value.ident == "default" {
85+
assert!(self.default.is_none(), "Default already set");
86+
87+
match name_value.lit {
88+
Lit::Str(ref val) => {
89+
let expr = val
90+
.parse::<Expr>()
91+
.expect("a valid expression for default argument");
92+
self.default = Some(expr);
93+
}
94+
_ => panic!("Expected string value for default argument"),
95+
}
96+
} else if name_value.ident == "optional" {
97+
match name_value.lit {
98+
Lit::Bool(ref val) => {
99+
self.optional = val.value;
100+
}
101+
_ => panic!("Expected boolean value for optional argument"),
102+
}
103+
} else {
104+
panic!("Unrecognised pyarg attribute '{}'", name_value.ident);
105+
}
106+
}
107+
_ => panic!("Bad syntax for first pyarg attribute argument"),
108+
};
109+
}
110+
}
111+
112+
fn generate_field(field: &Field) -> TokenStream2 {
113+
let mut pyarg_attrs = field
114+
.attrs
115+
.iter()
116+
.filter_map(ArgAttribute::from_attribute)
117+
.collect::<Vec<_>>();
118+
let attr = if pyarg_attrs.is_empty() {
119+
ArgAttribute {
120+
kind: ParameterKind::PositionalOrKeyword,
121+
default: None,
122+
optional: false,
123+
}
124+
} else if pyarg_attrs.len() == 1 {
125+
pyarg_attrs.remove(0)
126+
} else {
127+
panic!(
128+
"Multiple pyarg attributes on field '{}'",
129+
field.ident.as_ref().unwrap()
130+
);
131+
};
132+
133+
let name = &field.ident;
134+
let middle = quote! {
135+
.map(|x| crate::pyobject::TryFromObject::try_from_object(vm, x)).transpose()?
136+
};
137+
let ending = if let Some(default) = attr.default {
138+
quote! {
139+
.unwrap_or_else(|| #default)
140+
}
141+
} else if attr.optional {
142+
quote! {
143+
.map(crate::function::OptionalArg::Present)
144+
.unwrap_or(crate::function::OptionalArg::Missing)
145+
}
146+
} else {
147+
let err = match attr.kind {
148+
ParameterKind::PositionalOnly | ParameterKind::PositionalOrKeyword => quote! {
149+
crate::function::ArgumentError::TooFewArgs
150+
},
151+
ParameterKind::KeywordOnly => quote! {
152+
crate::function::ArgumentError::RequiredKeywordArgument(tringify!(#name))
153+
},
154+
};
155+
quote! {
156+
.ok_or_else(|| #err)?
157+
}
158+
};
159+
160+
match attr.kind {
161+
ParameterKind::PositionalOnly => {
162+
quote! {
163+
#name: args.take_positional()#middle#ending,
164+
}
165+
}
166+
ParameterKind::PositionalOrKeyword => {
167+
quote! {
168+
#name: args.take_positional_keyword(stringify!(#name))#middle#ending,
169+
}
170+
}
171+
ParameterKind::KeywordOnly => {
172+
quote! {
173+
#name: args.take_keyword(stringify!(#name))#middle#ending,
174+
}
175+
}
176+
}
177+
}
178+
179+
pub fn impl_from_args(input: DeriveInput) -> TokenStream2 {
180+
let rp_path = rustpython_path_derive(&input);
181+
let fields = match input.data {
182+
Data::Struct(ref data) => {
183+
match data.fields {
184+
Fields::Named(ref fields) => fields.named.iter().map(generate_field),
185+
Fields::Unnamed(_) | Fields::Unit => unimplemented!(), // TODO: better error message
186+
}
187+
}
188+
Data::Enum(_) | Data::Union(_) => unimplemented!(), // TODO: better error message
189+
};
190+
191+
let name = &input.ident;
192+
quote! {
193+
impl #rp_path::function::FromArgs for #name {
194+
fn from_args(
195+
vm: &crate::vm::VirtualMachine,
196+
args: &mut crate::function::PyFuncArgs
197+
) -> Result<Self, crate::function::ArgumentError> {
198+
Ok(#name { #(#fields)* })
199+
}
200+
}
201+
}
202+
}

0 commit comments

Comments
 (0)