1
+ use itertools:: Itertools ;
1
2
use stdx:: { format_to, to_lower_snake_case} ;
2
3
use syntax:: ast:: VisibilityOwner ;
3
4
use syntax:: ast:: { self , AstNode , NameOwner } ;
@@ -88,14 +89,104 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) ->
88
89
)
89
90
}
90
91
92
+ // Assist: generate_enum_into_method
93
+ //
94
+ // Generate an `into_` method for an enum variant.
95
+ //
96
+ // ```
97
+ // enum Value {
98
+ // Number(i32),
99
+ // Text(String)$0,
100
+ // }
101
+ // ```
102
+ // ->
103
+ // ```
104
+ // enum Value {
105
+ // Number(i32),
106
+ // Text(String),
107
+ // }
108
+ //
109
+ // impl Value {
110
+ // fn into_text(self) -> Option<String> {
111
+ // if let Self::Text(v) = self {
112
+ // Some(v)
113
+ // } else {
114
+ // None
115
+ // }
116
+ // }
117
+ // }
118
+ // ```
119
+ pub ( crate ) fn generate_enum_into_method ( acc : & mut Assists , ctx : & AssistContext ) -> Option < ( ) > {
120
+ let variant = ctx. find_node_at_offset :: < ast:: Variant > ( ) ?;
121
+ let variant_name = variant. name ( ) ?;
122
+ let parent_enum = ast:: Adt :: Enum ( variant. parent_enum ( ) ) ;
123
+ let variant_kind = variant_kind ( & variant) ;
124
+
125
+ let fn_name = format ! ( "into_{}" , & to_lower_snake_case( variant_name. text( ) ) ) ;
126
+
127
+ // Return early if we've found an existing new fn
128
+ let impl_def = find_struct_impl (
129
+ & ctx,
130
+ & parent_enum,
131
+ & fn_name,
132
+ ) ?;
133
+
134
+ let field_type = variant_kind. single_field_type ( ) ?;
135
+ let ( pattern_suffix, bound_name) = variant_kind. binding_pattern ( ) ?;
136
+
137
+ let target = variant. syntax ( ) . text_range ( ) ;
138
+ acc. add (
139
+ AssistId ( "generate_enum_into_method" , AssistKind :: Generate ) ,
140
+ "Generate an `into_` method for an enum variant" ,
141
+ target,
142
+ |builder| {
143
+ let mut buf = String :: with_capacity ( 512 ) ;
144
+
145
+ if impl_def. is_some ( ) {
146
+ buf. push ( '\n' ) ;
147
+ }
148
+
149
+ let vis = parent_enum. visibility ( ) . map_or ( String :: new ( ) , |v| format ! ( "{} " , v) ) ;
150
+ format_to ! (
151
+ buf,
152
+ " {}fn {}(self) -> Option<{}> {{
153
+ if let Self::{}{} = self {{
154
+ Some({})
155
+ }} else {{
156
+ None
157
+ }}
158
+ }}" ,
159
+ vis,
160
+ fn_name,
161
+ field_type. syntax( ) ,
162
+ variant_name,
163
+ pattern_suffix,
164
+ bound_name,
165
+ ) ;
166
+
167
+ let start_offset = impl_def
168
+ . and_then ( |impl_def| find_impl_block_end ( impl_def, & mut buf) )
169
+ . unwrap_or_else ( || {
170
+ buf = generate_impl_text ( & parent_enum, & buf) ;
171
+ parent_enum. syntax ( ) . text_range ( ) . end ( )
172
+ } ) ;
173
+
174
+ builder. insert ( start_offset, buf) ;
175
+ } ,
176
+ )
177
+ }
178
+
91
179
enum VariantKind {
92
180
Unit ,
93
181
/// Tuple with a single field
94
- NewtypeTuple ,
182
+ NewtypeTuple { ty : Option < ast :: Type > } ,
95
183
/// Tuple with 0 or more than 2 fields
96
184
Tuple ,
97
185
/// Record with a single field
98
- NewtypeRecord { field_name : Option < ast:: Name > } ,
186
+ NewtypeRecord {
187
+ field_name : Option < ast:: Name > ,
188
+ field_type : Option < ast:: Type > ,
189
+ } ,
99
190
/// Record with 0 or more than 2 fields
100
191
Record ,
101
192
}
@@ -104,27 +195,57 @@ impl VariantKind {
104
195
fn pattern_suffix ( & self ) -> & ' static str {
105
196
match self {
106
197
VariantKind :: Unit => "" ,
107
- VariantKind :: NewtypeTuple |
198
+ VariantKind :: NewtypeTuple { .. } |
108
199
VariantKind :: Tuple => "(..)" ,
109
200
VariantKind :: NewtypeRecord { .. } |
110
201
VariantKind :: Record => " { .. }" ,
111
202
}
112
203
}
204
+
205
+ fn binding_pattern ( & self ) -> Option < ( String , String ) > {
206
+ match self {
207
+ VariantKind :: Unit |
208
+ VariantKind :: Tuple |
209
+ VariantKind :: Record |
210
+ VariantKind :: NewtypeRecord { field_name : None , .. } => None ,
211
+ VariantKind :: NewtypeTuple { .. } => {
212
+ Some ( ( "(v)" . to_owned ( ) , "v" . to_owned ( ) ) )
213
+ }
214
+ VariantKind :: NewtypeRecord { field_name : Some ( name) , .. } => {
215
+ Some ( (
216
+ format ! ( " {{ {} }}" , name. syntax( ) ) ,
217
+ name. syntax ( ) . to_string ( ) ,
218
+ ) )
219
+ }
220
+ }
221
+ }
222
+
223
+ fn single_field_type ( & self ) -> Option < & ast:: Type > {
224
+ match self {
225
+ VariantKind :: Unit |
226
+ VariantKind :: Tuple |
227
+ VariantKind :: Record => None ,
228
+ VariantKind :: NewtypeTuple { ty } => ty. as_ref ( ) ,
229
+ VariantKind :: NewtypeRecord { field_type, .. } => field_type. as_ref ( ) ,
230
+ }
231
+ }
113
232
}
114
233
115
234
fn variant_kind ( variant : & ast:: Variant ) -> VariantKind {
116
235
match variant. kind ( ) {
117
236
ast:: StructKind :: Record ( record) => {
118
- if record. fields ( ) . count ( ) == 1 {
119
- let field_name = record. fields ( ) . nth ( 0 ) . unwrap ( ) . name ( ) ;
120
- VariantKind :: NewtypeRecord { field_name }
237
+ if let Some ( ( single_field, ) ) = record. fields ( ) . collect_tuple ( ) {
238
+ let field_name = single_field. name ( ) ;
239
+ let field_type = single_field. ty ( ) ;
240
+ VariantKind :: NewtypeRecord { field_name, field_type }
121
241
} else {
122
242
VariantKind :: Record
123
243
}
124
244
}
125
245
ast:: StructKind :: Tuple ( tuple) => {
126
- if tuple. fields ( ) . count ( ) == 1 {
127
- VariantKind :: NewtypeTuple
246
+ if let Some ( ( single_field, ) ) = tuple. fields ( ) . collect_tuple ( ) {
247
+ let ty = single_field. ty ( ) ;
248
+ VariantKind :: NewtypeTuple { ty }
128
249
} else {
129
250
VariantKind :: Tuple
130
251
}
@@ -139,12 +260,8 @@ mod tests {
139
260
140
261
use super :: * ;
141
262
142
- fn check_not_applicable ( ra_fixture : & str ) {
143
- check_assist_not_applicable ( generate_enum_is_method, ra_fixture)
144
- }
145
-
146
263
#[ test]
147
- fn test_generate_enum_match_from_variant ( ) {
264
+ fn test_generate_enum_is_from_variant ( ) {
148
265
check_assist (
149
266
generate_enum_is_method,
150
267
r#"
@@ -169,8 +286,9 @@ impl Variant {
169
286
}
170
287
171
288
#[ test]
172
- fn test_generate_enum_match_already_implemented ( ) {
173
- check_not_applicable (
289
+ fn test_generate_enum_is_already_implemented ( ) {
290
+ check_assist_not_applicable (
291
+ generate_enum_is_method,
174
292
r#"
175
293
enum Variant {
176
294
Undefined,
@@ -187,7 +305,7 @@ impl Variant {
187
305
}
188
306
189
307
#[ test]
190
- fn test_generate_enum_match_from_tuple_variant ( ) {
308
+ fn test_generate_enum_is_from_tuple_variant ( ) {
191
309
check_assist (
192
310
generate_enum_is_method,
193
311
r#"
@@ -212,7 +330,7 @@ impl Variant {
212
330
}
213
331
214
332
#[ test]
215
- fn test_generate_enum_match_from_record_variant ( ) {
333
+ fn test_generate_enum_is_from_record_variant ( ) {
216
334
check_assist (
217
335
generate_enum_is_method,
218
336
r#"
@@ -237,7 +355,7 @@ impl Variant {
237
355
}
238
356
239
357
#[ test]
240
- fn test_generate_enum_match_from_variant_with_one_variant ( ) {
358
+ fn test_generate_enum_is_from_variant_with_one_variant ( ) {
241
359
check_assist (
242
360
generate_enum_is_method,
243
361
r#"enum Variant { Undefi$0ned }"# ,
@@ -254,7 +372,7 @@ impl Variant {
254
372
}
255
373
256
374
#[ test]
257
- fn test_generate_enum_match_from_variant_with_visibility_marker ( ) {
375
+ fn test_generate_enum_is_from_variant_with_visibility_marker ( ) {
258
376
check_assist (
259
377
generate_enum_is_method,
260
378
r#"
@@ -279,7 +397,7 @@ impl Variant {
279
397
}
280
398
281
399
#[ test]
282
- fn test_multiple_generate_enum_match_from_variant ( ) {
400
+ fn test_multiple_generate_enum_is_from_variant ( ) {
283
401
check_assist (
284
402
generate_enum_is_method,
285
403
r#"
@@ -311,6 +429,113 @@ impl Variant {
311
429
fn is_major(&self) -> bool {
312
430
matches!(self, Self::Major)
313
431
}
432
+ }"# ,
433
+ ) ;
434
+ }
435
+
436
+ #[ test]
437
+ fn test_generate_enum_into_tuple_variant ( ) {
438
+ check_assist (
439
+ generate_enum_into_method,
440
+ r#"
441
+ enum Value {
442
+ Number(i32),
443
+ Text(String)$0,
444
+ }"# ,
445
+ r#"enum Value {
446
+ Number(i32),
447
+ Text(String),
448
+ }
449
+
450
+ impl Value {
451
+ fn into_text(self) -> Option<String> {
452
+ if let Self::Text(v) = self {
453
+ Some(v)
454
+ } else {
455
+ None
456
+ }
457
+ }
458
+ }"# ,
459
+ ) ;
460
+ }
461
+
462
+ #[ test]
463
+ fn test_generate_enum_into_already_implemented ( ) {
464
+ check_assist_not_applicable (
465
+ generate_enum_into_method,
466
+ r#"enum Value {
467
+ Number(i32),
468
+ Text(String)$0,
469
+ }
470
+
471
+ impl Value {
472
+ fn into_text(self) -> Option<String> {
473
+ if let Self::Text(v) = self {
474
+ Some(v)
475
+ } else {
476
+ None
477
+ }
478
+ }
479
+ }"# ,
480
+ ) ;
481
+ }
482
+
483
+ #[ test]
484
+ fn test_generate_enum_into_unit_variant ( ) {
485
+ check_assist_not_applicable (
486
+ generate_enum_into_method,
487
+ r#"enum Value {
488
+ Number(i32),
489
+ Text(String),
490
+ Unit$0,
491
+ }"# ,
492
+ ) ;
493
+ }
494
+
495
+ #[ test]
496
+ fn test_generate_enum_into_record_with_multiple_fields ( ) {
497
+ check_assist_not_applicable (
498
+ generate_enum_into_method,
499
+ r#"enum Value {
500
+ Number(i32),
501
+ Text(String),
502
+ Both { first: i32, second: String }$0,
503
+ }"# ,
504
+ ) ;
505
+ }
506
+
507
+ #[ test]
508
+ fn test_generate_enum_into_tuple_with_multiple_fields ( ) {
509
+ check_assist_not_applicable (
510
+ generate_enum_into_method,
511
+ r#"enum Value {
512
+ Number(i32),
513
+ Text(String, String)$0,
514
+ }"# ,
515
+ ) ;
516
+ }
517
+
518
+ #[ test]
519
+ fn test_generate_enum_into_record_variant ( ) {
520
+ check_assist (
521
+ generate_enum_into_method,
522
+ r#"enum Value {
523
+ Number(i32),
524
+ Text { text: String }$0,
525
+ }"# ,
526
+ r#"enum Value {
527
+ Number(i32),
528
+ Text { text: String },
529
+ }
530
+
531
+ impl Value {
532
+ fn into_text(self) -> Option<String> {
533
+ if let Self::Text { text } = self {
534
+ Some(text)
535
+ } else {
536
+ None
537
+ }
538
+ }
314
539
}"# ,
315
540
) ;
316
541
}
0 commit comments