Skip to content

Commit b3ff141

Browse files
committed
vec support
1 parent ad4cb82 commit b3ff141

File tree

3 files changed

+162
-32
lines changed

3 files changed

+162
-32
lines changed

macro/src/lib.rs

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ pub fn attribute_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStre
5050

5151
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
5252

53-
let mut options: Punctuated<TokenStream, Token!(,)> = Punctuated::new();
53+
let mut options_ty: Punctuated<TokenStream, Token!(,)> = Punctuated::new();
54+
let mut options_creation: Punctuated<TokenStream, Token!(,)> = Punctuated::new();
5455
let mut parsing: Punctuated<TokenStream, Token!(,)> = Punctuated::new();
5556
let mut option_assignments: Punctuated<TokenStream, Token!(;)> = Punctuated::new();
5657
let mut assignments: Punctuated<TokenStream, Token!(,)> = Punctuated::new();
@@ -62,10 +63,7 @@ pub fn attribute_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStre
6263
..
6364
}) => {
6465
for Field {
65-
attrs,
66-
ident,
67-
ty,
68-
..
66+
attrs, ident, ty, ..
6967
} in named.into_iter()
7068
{
7169
let default: bool =
@@ -99,18 +97,19 @@ pub fn attribute_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStre
9997
let ident = ident.expect("named struct fields have idents");
10098
let ident_str = ident.to_string();
10199

102-
options.push(quote!(#ident: Option<#ty>));
100+
options_ty
101+
.push(quote!(#ident: Option<<#ty as ::attribute_derive::ConvertParsed>::Type>));
102+
options_creation.push(quote!(#ident: None));
103103

104104
let error1 = format!("`{ident}` is specified multiple times");
105105
let error2 = format!("`{ident}` was already specified");
106106
option_assignments.push(quote! {
107107
match (&self.#ident, __other.#ident) {
108108
(#none, __value @ #some(_)) => self.#ident = __value,
109109
(#some(__first), #some(__second)) => {
110-
let mut __error =
111-
#syn::Error::new_spanned(__first, #error1);
112-
__error.combine(#syn::Error::new_spanned(
113-
__second,
110+
let mut __error = <<#ty as ::attribute_derive::ConvertParsed>::Type as ::attribute_derive::Error>::error(__first, #error1);
111+
__error.combine(<<#ty as ::attribute_derive::ConvertParsed>::Type as ::attribute_derive::Error>::error(
112+
&__second,
114113
#error2,
115114
));
116115
return #err(__error);
@@ -122,21 +121,23 @@ pub fn attribute_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStre
122121
parsing.push(quote! {
123122
#ident_str => {
124123
__options.#ident = #some(
125-
::attribute_derive::ConvertParsed::convert(__input.parse()?)?
124+
// ::attribute_derive::ConvertParsed::convert(__input.parse()?)?
125+
// TODO FQN
126+
__input.parse()?
126127
);
127128
}
128129
});
129130

130131
let error = format!("Mandatory `{ident}` was not specified via the attributes.");
131132
assignments.push(if default {
132133
quote! {
133-
#ident: __options.#ident.unwrap_or_default()
134+
#ident: __options.#ident.map(|t| ::attribute_derive::ConvertParsed::convert(t)).unwrap_or_else(|| #ok(<#ty as core::default::Default>::default()))?
134135
}
135136
} else {
136137
quote! {
137-
#ident: __options.#ident.ok_or_else(||
138+
#ident: __options.#ident.map(|t| ::attribute_derive::ConvertParsed::convert(t)).ok_or_else(||
138139
#syn::Error::new(#pm2::Span::call_site(), #error)
139-
)?
140+
)??
140141
}
141142
});
142143

@@ -154,15 +155,14 @@ pub fn attribute_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStre
154155
last
155156
)
156157
} else {
157-
format!("Only `{}` is allowd field", possible_variables[0])
158+
format!("Expected supported field {}", possible_variables[0])
158159
};
159160

160161
quote! {
161162
impl #impl_generics ::attribute_derive::Attribute for #ident #ty_generics #where_clause {
162163
fn from_attributes(__attrs: impl ::core::iter::IntoIterator<Item = #syn::Attribute>) -> #syn::Result<Self>{
163-
#[derive(::core::default::Default)]
164164
struct __Options{
165-
#options
165+
#options_ty
166166
}
167167
impl __Options {
168168
fn extend_with(&mut self, __other:Self) -> #syn::Result<()>{
@@ -172,7 +172,9 @@ pub fn attribute_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStre
172172
}
173173
impl #syn::parse::Parse for __Options {
174174
fn parse(__input: #syn::parse::ParseStream<'_>) -> #syn::Result<Self> {
175-
let mut __options = Self::default();
175+
let mut __options = __Options{
176+
#options_creation
177+
};
176178
loop {
177179
if __input.is_empty() {
178180
break;
@@ -213,7 +215,9 @@ pub fn attribute_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStre
213215
Ok(__options)
214216
}
215217
}
216-
let mut __options = __Options::default();
218+
let mut __options = __Options{
219+
#options_creation
220+
};
217221
for __attribute in __attrs {
218222
if __attribute.path.is_ident(#attribute_ident) {
219223
__options.extend_with(__attribute.parse_args()?)?;

src/lib.rs

Lines changed: 74 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@
33
//! key=value. One important thing to keep in mind, is the limitation on values being valid
44
//! expresions so we can rely on syn for the , splitting :D
55
6-
use proc_macro2::Literal;
6+
use std::fmt::Display;
7+
8+
use proc_macro2::{Literal, Span};
79
pub use r#macro::Attribute;
8-
use syn::{Expr, Lit, LitBool, LitByteStr, LitChar, LitFloat, LitInt, LitStr, Path, Result, Type};
10+
use syn::{
11+
bracketed, parse::Parse, punctuated::Punctuated, Expr, Lit, LitBool,
12+
LitByteStr, LitChar, LitFloat, LitInt, LitStr, Path, Result, Token, Type, __private::ToTokens,
13+
};
914

1015
pub mod __private {
1116
pub use proc_macro2;
@@ -19,24 +24,42 @@ where
1924
fn from_attributes(attrs: impl IntoIterator<Item = syn::Attribute>) -> Result<Self>;
2025
}
2126

22-
pub trait ConvertParsed<T>
27+
pub trait ConvertParsed
2328
where
2429
Self: Sized,
30+
Self::Type: Error,
31+
{
32+
type Type;
33+
fn convert(value: Self::Type) -> Result<Self>;
34+
}
35+
36+
/// Helper trait to generate sensible errors
37+
pub trait Error {
38+
fn error(&self, message: impl Display) -> syn::Error;
39+
}
40+
41+
impl<T> Error for T
42+
where
43+
T: ToTokens,
2544
{
26-
fn convert(s: T) -> Result<Self>;
45+
fn error(&self, message: impl Display) -> syn::Error {
46+
syn::Error::new_spanned(self, message)
47+
}
2748
}
2849

2950
macro_rules! convert_parsed {
3051
($type:path) => {
31-
impl ConvertParsed<$type> for $type {
52+
impl ConvertParsed for $type {
53+
type Type = $type;
3254
fn convert(s: Self) -> Result<Self> {
3355
Ok(s)
3456
}
3557
}
3658
};
3759
[$($type:path),*] => {
3860
$(
39-
impl ConvertParsed<$type> for $type {
61+
impl ConvertParsed for $type {
62+
type Type = $type;
4063
fn convert(s: Self) -> Result<Self> {
4164
Ok(s)
4265
}
@@ -52,7 +75,8 @@ macro_rules! convert_parsed {
5275
};
5376
($from:path => $($to:path),+ : $with:path ) => {
5477
$(
55-
impl ConvertParsed<$from> for $to {
78+
impl ConvertParsed for $to {
79+
type Type = $from;
5680
fn convert(value: $from) -> Result<$to> {
5781
Ok($with(&value))
5882
}
@@ -61,7 +85,8 @@ macro_rules! convert_parsed {
6185
};
6286
($from:path => $($to:path),+ :? $with:path ) => {
6387
$(
64-
impl ConvertParsed<$from> for $to {
88+
impl ConvertParsed for $to {
89+
type Type = $from;
6590
fn convert(value: $from) -> Result<$to> {
6691
$with(&value)
6792
}
@@ -70,15 +95,53 @@ macro_rules! convert_parsed {
7095
};
7196
}
7297

73-
impl<Output, Parsed> ConvertParsed<Parsed> for Option<Output>
98+
impl<Output, Parsed> ConvertParsed for Option<Output>
7499
where
75-
Output: ConvertParsed<Parsed>,
100+
Output: ConvertParsed<Type = Parsed>,
101+
Parsed: Error,
76102
{
103+
type Type = Parsed;
77104
fn convert(s: Parsed) -> Result<Self> {
78105
Ok(Some(ConvertParsed::convert(s)?))
79106
}
80107
}
81108

109+
impl<Output, Parsed> ConvertParsed for Vec<Output>
110+
where
111+
Output: ConvertParsed<Type = Parsed>,
112+
{
113+
type Type = Array<Parsed>;
114+
fn convert(array: Array<Parsed>) -> Result<Self> {
115+
array.data.into_iter().map(ConvertParsed::convert).collect()
116+
}
117+
}
118+
119+
/// Helper struct to parse array literals
120+
pub struct Array<T> {
121+
data: Vec<T>,
122+
span: Span,
123+
}
124+
125+
impl<T> Parse for Array<T>
126+
where
127+
T: Parse,
128+
{
129+
fn parse(input: syn::parse::ParseStream) -> Result<Self> {
130+
let content;
131+
let b = bracketed!(content in input);
132+
let i = Punctuated::<T, Token!(,)>::parse_terminated(&content)?;
133+
Ok(Self {
134+
data: i.into_iter().collect(),
135+
span: b.span,
136+
})
137+
}
138+
}
139+
140+
impl<T> Error for Array<T> {
141+
fn error(&self, message: impl Display) -> syn::Error {
142+
syn::Error::new(self.span, message)
143+
}
144+
}
82145

83146
convert_parsed!(Type);
84147
convert_parsed!(Path);
@@ -87,7 +150,7 @@ convert_parsed![LitStr, LitByteStr, LitChar, LitInt, LitFloat, LitBool, Literal]
87150
convert_parsed!(Expr);
88151

89152
convert_parsed!(LitStr => String: LitStr::value);
90-
convert_parsed!(LitByteStr => Vec<u8>: LitByteStr::value);
153+
// TODO convert_parsed!(LitByteStr => Vec<u8>: LitByteStr::value);
91154
convert_parsed!(LitChar => char: LitChar::value);
92155
convert_parsed!(LitInt => u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize:? LitInt::base10_parse);
93156
convert_parsed!(LitFloat => f32, f64:? LitFloat::base10_parse);
@@ -327,4 +390,3 @@ convert_parsed!(LitBool => bool: LitBool::value);
327390
// impl Parse for While
328391
// impl Parse for Yield
329392
// impl Parse for Nothing
330-

tests/derive.rs

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ fn test() {
1616
od: Option<Type>,
1717
d: Type,
1818
e: Expr,
19+
f: Vec<Type>
1920
}
2021

2122
let parsed = Test::from_attributes([
22-
parse_quote!(#[test(b="hi", c="ho", oc="xD", d=(), e=if true{ "a" } else { "b" })]),
23+
parse_quote!(#[test(b="hi", c="ho", oc="xD", d=(), e=if true{ "a" } else { "b" }, f= [(), Debug])]),
2324
])
2425
.unwrap();
2526
assert_eq!(parsed.b.value(), "hi");
@@ -28,13 +29,15 @@ fn test() {
2829
assert!(parsed.od.is_none());
2930
assert!(matches!(parsed.d, Type::Tuple(_)));
3031
assert!(matches!(parsed.e, Expr::If(_)));
32+
assert!(parsed.f.len() == 2);
3133
}
3234

3335
#[test]
3436
fn error() {
3537
#[derive(Attribute, Debug)]
3638
#[attribute(test)]
3739
struct Test {
40+
#[allow(dead_code)]
3841
s: String,
3942
}
4043

@@ -51,4 +54,65 @@ fn error() {
5154
.to_string(),
5255
"expected string literal"
5356
);
57+
58+
assert_eq!(
59+
Test::from_attributes([parse_quote!(#[test(invalid_attribute)])])
60+
.unwrap_err()
61+
.to_string(),
62+
"unexpected end of input, Expected assignment `=`"
63+
);
64+
65+
assert_eq!(
66+
Test::from_attributes([parse_quote!(#[test(invalid_attribute="")])])
67+
.unwrap_err()
68+
.to_string(),
69+
"Expected supported field `s`"
70+
);
71+
}
72+
73+
#[test]
74+
fn error2() {
75+
#[derive(Attribute, Debug)]
76+
#[attribute(test)]
77+
struct Test {
78+
#[allow(dead_code)]
79+
a: f32,
80+
b: u8
81+
}
82+
83+
assert_eq!(
84+
Test::from_attributes([parse_quote!(#[test()])])
85+
.unwrap_err()
86+
.to_string(),
87+
"Mandatory `a` was not specified via the attributes."
88+
);
89+
90+
assert_eq!(
91+
Test::from_attributes([parse_quote!(#[test(a="")])])
92+
.unwrap_err()
93+
.to_string(),
94+
"expected floating point literal"
95+
);
96+
97+
assert_eq!(
98+
Test::from_attributes([parse_quote!(#[test(a=0., b=10.)])])
99+
.unwrap_err()
100+
.to_string(),
101+
"expected integer literal"
102+
);
103+
104+
// FIXME I expected this to fail....
105+
// assert_eq!(
106+
// Test::from_attributes([parse_quote!(#[test(a=1.7976931348623157E+308f64,b=0)])])
107+
// .unwrap_err()
108+
// .to_string(),
109+
// "unexpected end of input, Expected assignment `=`"
110+
// );
111+
112+
assert_eq!(
113+
Test::from_attributes([parse_quote!(#[test(a=0.,b=1000000)])])
114+
.unwrap_err()
115+
.to_string(),
116+
"number too large to fit in target type"
117+
);
54118
}

0 commit comments

Comments
 (0)