@@ -7,48 +7,169 @@ pub use schemars_to_serde::process_serde_attrs;
7
7
use proc_macro2:: { Group , Span , TokenStream , TokenTree } ;
8
8
use serde_derive_internals:: Ctxt ;
9
9
use syn:: parse:: { self , Parse } ;
10
+ use syn:: Meta :: { List , NameValue } ;
11
+ use syn:: MetaNameValue ;
12
+ use syn:: NestedMeta :: { Lit , Meta } ;
10
13
11
- pub fn get_with_from_attrs (
12
- attrs : & [ syn:: Attribute ] ,
13
- errors : & Ctxt ,
14
- ) -> Result < Option < syn:: Type > , ( ) > {
15
- attrs
16
- . iter ( )
17
- . filter ( |at| match at. path . get_ident ( ) {
18
- // FIXME this is relying on order of attributes (schemars before serde) from schemars_to_serde.rs
19
- Some ( i) => i == "schemars" || i == "serde" ,
20
- None => false ,
21
- } )
22
- . filter_map ( get_with_from_attr)
23
- . next ( )
24
- . map_or ( Ok ( None ) , |lit| match parse_lit_str :: < syn:: Type > ( & lit) {
25
- Ok ( t) => Ok ( Some ( t) ) ,
26
- Err ( e) => {
27
- errors. error_spanned_by ( lit, e) ;
28
- Err ( ( ) )
29
- }
30
- } )
14
+ #[ derive( Debug , Default ) ]
15
+ pub struct Attrs {
16
+ pub with : Option < WithAttr > ,
17
+ pub title : Option < String > ,
18
+ pub description : Option < String > ,
19
+ // TODO pub example: Option<syn::Path>,
20
+ }
21
+
22
+ #[ derive( Debug ) ]
23
+ pub enum WithAttr {
24
+ Type ( syn:: Type ) ,
25
+ _Function( syn:: Path ) ,
31
26
}
32
27
33
- fn get_with_from_attr ( attr : & syn:: Attribute ) -> Option < syn:: LitStr > {
34
- use syn:: * ;
35
- let nested_metas = match attr. parse_meta ( ) {
36
- Ok ( Meta :: List ( meta) ) => meta. nested ,
37
- _ => return None ,
38
- } ;
39
- for nm in nested_metas {
40
- if let NestedMeta :: Meta ( Meta :: NameValue ( MetaNameValue {
41
- path,
42
- lit : Lit :: Str ( with) ,
43
- ..
44
- } ) ) = nm
28
+ impl Attrs {
29
+ pub fn new ( attrs : & [ syn:: Attribute ] , errors : & Ctxt ) -> Self {
30
+ let ( title, description) = doc:: get_title_and_desc_from_doc ( attrs) ;
31
+ Attrs {
32
+ title,
33
+ description,
34
+ ..Attrs :: default ( )
35
+ }
36
+ . populate ( attrs, "schemars" , false , errors)
37
+ . populate ( attrs, "serde" , true , errors)
38
+ }
39
+
40
+ fn populate (
41
+ mut self ,
42
+ attrs : & [ syn:: Attribute ] ,
43
+ attr_type : & ' static str ,
44
+ ignore_errors : bool ,
45
+ errors : & Ctxt ,
46
+ ) -> Self {
47
+ let duplicate_error = |meta : & MetaNameValue | {
48
+ if !ignore_errors {
49
+ let msg = format ! (
50
+ "duplicate schemars attribute `{}`" ,
51
+ meta. path. get_ident( ) . unwrap( )
52
+ ) ;
53
+ errors. error_spanned_by ( meta, msg)
54
+ }
55
+ } ;
56
+ let mutual_exclusive_error = |meta : & MetaNameValue , other : & str | {
57
+ if !ignore_errors {
58
+ let msg = format ! (
59
+ "schemars attribute cannot contain both `{}` and `{}`" ,
60
+ meta. path. get_ident( ) . unwrap( ) ,
61
+ other,
62
+ ) ;
63
+ errors. error_spanned_by ( meta, msg)
64
+ }
65
+ } ;
66
+
67
+ for meta_item in attrs
68
+ . iter ( )
69
+ . flat_map ( |attr| get_meta_items ( attr, attr_type, errors) )
70
+ . flatten ( )
45
71
{
46
- if path. is_ident ( "with" ) {
47
- return Some ( with) ;
72
+ match & meta_item {
73
+ Meta ( NameValue ( m) ) if m. path . is_ident ( "with" ) => {
74
+ if let Ok ( ty) = parse_lit_into_ty ( errors, attr_type, "with" , & m. lit ) {
75
+ match self . with {
76
+ Some ( WithAttr :: Type ( _) ) => duplicate_error ( m) ,
77
+ Some ( WithAttr :: _Function( _) ) => {
78
+ mutual_exclusive_error ( m, "schema_with" )
79
+ }
80
+ None => self . with = Some ( WithAttr :: Type ( ty) ) ,
81
+ }
82
+ }
83
+ }
84
+
85
+ Meta ( _meta_item) => {
86
+ // TODO uncomment this for 0.8.0 (breaking change)
87
+ // https://github.com/GREsau/schemars/issues/18
88
+ // if !ignore_errors {
89
+ // let path = meta_item
90
+ // .path()
91
+ // .into_token_stream()
92
+ // .to_string()
93
+ // .replace(' ', "");
94
+ // errors.error_spanned_by(
95
+ // meta_item.path(),
96
+ // format!("unknown schemars container attribute `{}`", path),
97
+ // );
98
+ // }
99
+ }
100
+
101
+ Lit ( _lit) => {
102
+ // TODO uncomment this for 0.8.0 (breaking change)
103
+ // https://github.com/GREsau/schemars/issues/18
104
+ // if !ignore_errors {
105
+ // errors.error_spanned_by(
106
+ // lit,
107
+ // "unexpected literal in schemars container attribute",
108
+ // );
109
+ // }
110
+ }
48
111
}
49
112
}
113
+ self
114
+ }
115
+ }
116
+
117
+ fn get_meta_items (
118
+ attr : & syn:: Attribute ,
119
+ attr_type : & ' static str ,
120
+ errors : & Ctxt ,
121
+ ) -> Result < Vec < syn:: NestedMeta > , ( ) > {
122
+ if !attr. path . is_ident ( attr_type) {
123
+ return Ok ( Vec :: new ( ) ) ;
124
+ }
125
+
126
+ match attr. parse_meta ( ) {
127
+ Ok ( List ( meta) ) => Ok ( meta. nested . into_iter ( ) . collect ( ) ) ,
128
+ Ok ( other) => {
129
+ errors. error_spanned_by ( other, format ! ( "expected #[{}(...)]" , attr_type) ) ;
130
+ Err ( ( ) )
131
+ }
132
+ Err ( err) => {
133
+ errors. error_spanned_by ( attr, err) ;
134
+ Err ( ( ) )
135
+ }
50
136
}
51
- None
137
+ }
138
+
139
+ fn get_lit_str < ' a > (
140
+ cx : & Ctxt ,
141
+ attr_type : & ' static str ,
142
+ meta_item_name : & ' static str ,
143
+ lit : & ' a syn:: Lit ,
144
+ ) -> Result < & ' a syn:: LitStr , ( ) > {
145
+ if let syn:: Lit :: Str ( lit) = lit {
146
+ Ok ( lit)
147
+ } else {
148
+ cx. error_spanned_by (
149
+ lit,
150
+ format ! (
151
+ "expected {} attribute to be a string: `{} = \" ...\" `" ,
152
+ attr_type, meta_item_name
153
+ ) ,
154
+ ) ;
155
+ Err ( ( ) )
156
+ }
157
+ }
158
+
159
+ fn parse_lit_into_ty (
160
+ cx : & Ctxt ,
161
+ attr_type : & ' static str ,
162
+ meta_item_name : & ' static str ,
163
+ lit : & syn:: Lit ,
164
+ ) -> Result < syn:: Type , ( ) > {
165
+ let string = get_lit_str ( cx, attr_type, meta_item_name, lit) ?;
166
+
167
+ parse_lit_str ( string) . map_err ( |_| {
168
+ cx. error_spanned_by (
169
+ lit,
170
+ format ! ( "failed to parse type: {} = {:?}" , attr_type, string. value( ) ) ,
171
+ )
172
+ } )
52
173
}
53
174
54
175
fn parse_lit_str < T > ( s : & syn:: LitStr ) -> parse:: Result < T >
0 commit comments