1
- use darling:: { util:: SpannedValue , Error , FromField , FromMeta } ;
2
- use k8s_version:: Version ;
3
- use proc_macro2:: Span ;
4
- use syn:: { Field , Ident , Path } ;
1
+ use darling:: { Error , FromField } ;
2
+ use syn:: { Field , Ident } ;
5
3
6
- use crate :: { attrs:: container:: ContainerAttributes , consts:: DEPRECATED_PREFIX } ;
4
+ use crate :: {
5
+ attrs:: common:: { ContainerAttributes , ItemAttributes } ,
6
+ consts:: DEPRECATED_FIELD_PREFIX ,
7
+ } ;
7
8
8
9
/// This struct describes all available field attributes, as well as the field
9
10
/// name to display better diagnostics.
@@ -29,43 +30,24 @@ use crate::{attrs::container::ContainerAttributes, consts::DEPRECATED_PREFIX};
29
30
and_then = FieldAttributes :: validate
30
31
) ]
31
32
pub ( crate ) struct FieldAttributes {
32
- pub ( crate ) ident : Option < Ident > ,
33
- pub ( crate ) added : Option < AddedAttributes > ,
34
-
35
- #[ darling( multiple, rename = "renamed" ) ]
36
- pub ( crate ) renames : Vec < RenamedAttributes > ,
37
-
38
- pub ( crate ) deprecated : Option < DeprecatedAttributes > ,
39
- }
40
-
41
- #[ derive( Clone , Debug , FromMeta ) ]
42
- pub ( crate ) struct AddedAttributes {
43
- pub ( crate ) since : SpannedValue < Version > ,
44
-
45
- #[ darling( rename = "default" , default = "default_default_fn" ) ]
46
- pub ( crate ) default_fn : SpannedValue < Path > ,
47
- }
48
-
49
- fn default_default_fn ( ) -> SpannedValue < Path > {
50
- SpannedValue :: new (
51
- syn:: parse_str ( "std::default::Default::default" ) . expect ( "internal error: path must parse" ) ,
52
- Span :: call_site ( ) ,
53
- )
54
- }
55
-
56
- #[ derive( Clone , Debug , FromMeta ) ]
57
- pub ( crate ) struct RenamedAttributes {
58
- pub ( crate ) since : SpannedValue < Version > ,
59
- pub ( crate ) from : SpannedValue < String > ,
60
- }
33
+ #[ darling( flatten) ]
34
+ pub ( crate ) common : ItemAttributes ,
61
35
62
- # [ derive ( Clone , Debug , FromMeta ) ]
63
- pub ( crate ) struct DeprecatedAttributes {
64
- pub ( crate ) since : SpannedValue < Version > ,
65
- pub ( crate ) note : SpannedValue < String > ,
36
+ // The ident (automatically extracted by darling) cannot be moved into the
37
+ // shared item attributes because for struct fields, the type is
38
+ // `Option<Ident>`, while for enum variants, the type is `Ident`.
39
+ pub ( crate ) ident : Option < Ident > ,
66
40
}
67
41
68
42
impl FieldAttributes {
43
+ // NOTE (@Techassi): Ideally, these validations should be moved to the
44
+ // ItemAttributes impl, because common validation like action combinations
45
+ // and action order can be validated without taking the type of attribute
46
+ // into account (field vs variant). However, we would loose access to the
47
+ // field / variant ident and as such, cannot display the error directly on
48
+ // the affected field / variant. This is a significant decrease in DX.
49
+ // See https://github.com/TedDriggs/darling/discussions/294
50
+
69
51
/// This associated function is called by darling (see and_then attribute)
70
52
/// after it successfully parsed the attribute. This allows custom
71
53
/// validation of the attribute which extends the validation already in
@@ -80,12 +62,6 @@ impl FieldAttributes {
80
62
errors. handle ( self . validate_action_order ( ) ) ;
81
63
errors. handle ( self . validate_field_name ( ) ) ;
82
64
83
- // Code quality validation
84
- errors. handle ( self . validate_deprecated_options ( ) ) ;
85
-
86
- // TODO (@Techassi): Add validation for renames so that renamed fields
87
- // match up and form a continous chain (eg. foo -> bar -> baz).
88
-
89
65
// TODO (@Techassi): Add hint if a field is added in the first version
90
66
// that it might be clever to remove the 'added' attribute.
91
67
@@ -107,7 +83,11 @@ impl FieldAttributes {
107
83
/// - `renamed` and `deprecated` using the same version: Again, the same
108
84
/// rules from above apply here as well.
109
85
fn validate_action_combinations ( & self ) -> Result < ( ) , Error > {
110
- match ( & self . added , & self . renames , & self . deprecated ) {
86
+ match (
87
+ & self . common . added ,
88
+ & self . common . renames ,
89
+ & self . common . deprecated ,
90
+ ) {
111
91
( Some ( added) , _, Some ( deprecated) ) if * added. since == * deprecated. since => {
112
92
Err ( Error :: custom (
113
93
"field cannot be marked as `added` and `deprecated` in the same version" ,
@@ -145,15 +125,15 @@ impl FieldAttributes {
145
125
/// - All `renamed` actions must use a greater version than `added` but a
146
126
/// lesser version than `deprecated`.
147
127
fn validate_action_order ( & self ) -> Result < ( ) , Error > {
148
- let added_version = self . added . as_ref ( ) . map ( |a| * a. since ) ;
149
- let deprecated_version = self . deprecated . as_ref ( ) . map ( |d| * d. since ) ;
128
+ let added_version = self . common . added . as_ref ( ) . map ( |a| * a. since ) ;
129
+ let deprecated_version = self . common . deprecated . as_ref ( ) . map ( |d| * d. since ) ;
150
130
151
131
// First, validate that the added version is less than the deprecated
152
132
// version.
153
133
// NOTE (@Techassi): Is this already covered by the code below?
154
134
if let ( Some ( added_version) , Some ( deprecated_version) ) = ( added_version, deprecated_version)
155
135
{
156
- if added_version >= deprecated_version {
136
+ if added_version > deprecated_version {
157
137
return Err ( Error :: custom ( format ! (
158
138
"field was marked as `added` in version `{added_version}` while being marked as `deprecated` in an earlier version `{deprecated_version}`"
159
139
) ) . with_span ( & self . ident ) ) ;
@@ -162,7 +142,7 @@ impl FieldAttributes {
162
142
163
143
// Now, iterate over all renames and ensure that their versions are
164
144
// between the added and deprecated version.
165
- if !self . renames . iter ( ) . all ( |r| {
145
+ if !self . common . renames . iter ( ) . all ( |r| {
166
146
added_version. map_or ( true , |a| a < * r. since )
167
147
&& deprecated_version. map_or ( true , |d| d > * r. since )
168
148
} ) {
@@ -185,20 +165,20 @@ impl FieldAttributes {
185
165
/// in their name. The prefix must not be included for fields which are
186
166
/// not deprecated.
187
167
fn validate_field_name ( & self ) -> Result < ( ) , Error > {
188
- let starts_with = self
168
+ let starts_with_deprecated = self
189
169
. ident
190
170
. as_ref ( )
191
- . unwrap ( )
171
+ . expect ( "internal error: to be validated fields must have a name" )
192
172
. to_string ( )
193
- . starts_with ( DEPRECATED_PREFIX ) ;
173
+ . starts_with ( DEPRECATED_FIELD_PREFIX ) ;
194
174
195
- if self . deprecated . is_some ( ) && !starts_with {
175
+ if self . common . deprecated . is_some ( ) && !starts_with_deprecated {
196
176
return Err ( Error :: custom (
197
177
"field was marked as `deprecated` and thus must include the `deprecated_` prefix in its name"
198
178
) . with_span ( & self . ident ) ) ;
199
179
}
200
180
201
- if self . deprecated . is_none ( ) && starts_with {
181
+ if self . common . deprecated . is_none ( ) && starts_with_deprecated {
202
182
return Err ( Error :: custom (
203
183
"field includes the `deprecated_` prefix in its name but is not marked as `deprecated`"
204
184
) . with_span ( & self . ident ) ) ;
@@ -207,22 +187,6 @@ impl FieldAttributes {
207
187
Ok ( ( ) )
208
188
}
209
189
210
- fn validate_deprecated_options ( & self ) -> Result < ( ) , Error > {
211
- // TODO (@Techassi): Make the field 'note' optional, because in the
212
- // future, the macro will generate parts of the deprecation note
213
- // automatically. The user-provided note will then be appended to the
214
- // auto-generated one.
215
-
216
- if let Some ( deprecated) = & self . deprecated {
217
- if deprecated. note . is_empty ( ) {
218
- return Err ( Error :: custom ( "deprecation note must not be empty" )
219
- . with_span ( & deprecated. note . span ( ) ) ) ;
220
- }
221
- }
222
-
223
- Ok ( ( ) )
224
- }
225
-
226
190
/// Validates that each field action version is present in the declared
227
191
/// container versions.
228
192
pub ( crate ) fn validate_versions (
@@ -233,7 +197,7 @@ impl FieldAttributes {
233
197
// NOTE (@Techassi): Can we maybe optimize this a little?
234
198
let mut errors = Error :: accumulator ( ) ;
235
199
236
- if let Some ( added) = & self . added {
200
+ if let Some ( added) = & self . common . added {
237
201
if !container_attrs
238
202
. versions
239
203
. iter ( )
@@ -246,7 +210,7 @@ impl FieldAttributes {
246
210
}
247
211
}
248
212
249
- for rename in & self . renames {
213
+ for rename in & self . common . renames {
250
214
if !container_attrs
251
215
. versions
252
216
. iter ( )
@@ -259,7 +223,7 @@ impl FieldAttributes {
259
223
}
260
224
}
261
225
262
- if let Some ( deprecated) = & self . deprecated {
226
+ if let Some ( deprecated) = & self . common . deprecated {
263
227
if !container_attrs
264
228
. versions
265
229
. iter ( )
0 commit comments