1- use std:: slice;
2-
31use ide_db:: assists:: GroupLabel ;
42use itertools:: Itertools ;
53use stdx:: to_lower_snake_case;
@@ -8,7 +6,7 @@ use syntax::ast::{self, AstNode, HasName};
86
97use crate :: {
108 AssistContext , AssistId , Assists ,
11- utils:: { add_method_to_adt, find_struct_impl} ,
9+ utils:: { add_method_to_adt, find_struct_impl, is_selected } ,
1210} ;
1311
1412// Assist: generate_enum_try_into_method
@@ -128,29 +126,22 @@ fn generate_enum_projection_method(
128126 } = props;
129127
130128 let variant = ctx. find_node_at_offset :: < ast:: Variant > ( ) ?;
131- let variant_name = variant. name ( ) ?;
132129 let parent_enum = ast:: Adt :: Enum ( variant. parent_enum ( ) ) ;
133-
134- let ( pattern_suffix, field_type, bound_name) = match variant. kind ( ) {
135- ast:: StructKind :: Record ( record) => {
136- let ( field, ) = record. fields ( ) . collect_tuple ( ) ?;
137- let name = field. name ( ) ?. to_string ( ) ;
138- let ty = field. ty ( ) ?;
139- let pattern_suffix = format ! ( " {{ {name} }}" ) ;
140- ( pattern_suffix, ty, name)
141- }
142- ast:: StructKind :: Tuple ( tuple) => {
143- let ( field, ) = tuple. fields ( ) . collect_tuple ( ) ?;
144- let ty = field. ty ( ) ?;
145- ( "(v)" . to_owned ( ) , ty, "v" . to_owned ( ) )
146- }
147- ast:: StructKind :: Unit => return None ,
148- } ;
149-
150- let fn_name = format ! ( "{fn_name_prefix}_{}" , & to_lower_snake_case( & variant_name. text( ) ) ) ;
130+ let variants = variant
131+ . parent_enum ( )
132+ . variant_list ( ) ?
133+ . variants ( )
134+ . filter ( |it| is_selected ( it, ctx. selection_trimmed ( ) , true ) )
135+ . collect :: < Vec < _ > > ( ) ;
136+ let methods = variants
137+ . iter ( )
138+ . map ( |variant| Method :: new ( variant, fn_name_prefix) )
139+ . collect :: < Option < Vec < _ > > > ( ) ?;
140+ let fn_names = methods. iter ( ) . map ( |it| it. fn_name . clone ( ) ) . collect :: < Vec < _ > > ( ) ;
141+ stdx:: never!( variants. is_empty( ) ) ;
151142
152143 // Return early if we've found an existing new fn
153- let impl_def = find_struct_impl ( ctx, & parent_enum, slice :: from_ref ( & fn_name ) ) ?;
144+ let impl_def = find_struct_impl ( ctx, & parent_enum, & fn_names ) ?;
154145
155146 let target = variant. syntax ( ) . text_range ( ) ;
156147 acc. add_group (
@@ -161,29 +152,66 @@ fn generate_enum_projection_method(
161152 |builder| {
162153 let vis = parent_enum. visibility ( ) . map_or ( String :: new ( ) , |v| format ! ( "{v} " ) ) ;
163154
164- let field_type_syntax = field_type . syntax ( ) ;
155+ let must_use = if ctx . config . assist_emit_must_use { "#[must_use] \n " } else { "" } ;
165156
166- let must_use = if ctx. config . assist_emit_must_use {
167- "#[must_use]\n "
168- } else {
169- ""
170- } ;
171-
172- let method = format ! (
173- " {must_use}{vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type_syntax}{return_suffix} {{
157+ let method = methods
158+ . iter ( )
159+ . map ( |Method { pattern_suffix, field_type, bound_name, fn_name, variant_name } | {
160+ format ! (
161+ " \
162+ {must_use}{vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type}{return_suffix} {{
174163 if let Self::{variant_name}{pattern_suffix} = self {{
175164 {happy_case}({bound_name})
176165 }} else {{
177166 {sad_case}
178167 }}
179168 }}"
180- ) ;
169+ )
170+ } )
171+ . join ( "\n \n " ) ;
181172
182173 add_method_to_adt ( builder, & parent_enum, impl_def, & method) ;
183174 } ,
184175 )
185176}
186177
178+ struct Method {
179+ pattern_suffix : String ,
180+ field_type : ast:: Type ,
181+ bound_name : String ,
182+ fn_name : String ,
183+ variant_name : ast:: Name ,
184+ }
185+
186+ impl Method {
187+ fn new ( variant : & ast:: Variant , fn_name_prefix : & str ) -> Option < Self > {
188+ let variant_name = variant. name ( ) ?;
189+ let fn_name = format ! ( "{fn_name_prefix}_{}" , & to_lower_snake_case( & variant_name. text( ) ) ) ;
190+
191+ match variant. kind ( ) {
192+ ast:: StructKind :: Record ( record) => {
193+ let ( field, ) = record. fields ( ) . collect_tuple ( ) ?;
194+ let name = field. name ( ) ?. to_string ( ) ;
195+ let field_type = field. ty ( ) ?;
196+ let pattern_suffix = format ! ( " {{ {name} }}" ) ;
197+ Some ( Method { pattern_suffix, field_type, bound_name : name, fn_name, variant_name } )
198+ }
199+ ast:: StructKind :: Tuple ( tuple) => {
200+ let ( field, ) = tuple. fields ( ) . collect_tuple ( ) ?;
201+ let field_type = field. ty ( ) ?;
202+ Some ( Method {
203+ pattern_suffix : "(v)" . to_owned ( ) ,
204+ field_type,
205+ bound_name : "v" . to_owned ( ) ,
206+ variant_name,
207+ fn_name,
208+ } )
209+ }
210+ ast:: StructKind :: Unit => None ,
211+ }
212+ }
213+ }
214+
187215#[ cfg( test) ]
188216mod tests {
189217 use crate :: tests:: { check_assist, check_assist_not_applicable} ;
@@ -216,6 +244,42 @@ impl Value {
216244 ) ;
217245 }
218246
247+ #[ test]
248+ fn test_generate_enum_multiple_try_into_tuple_variant ( ) {
249+ check_assist (
250+ generate_enum_try_into_method,
251+ r#"
252+ enum Value {
253+ Unit(()),
254+ $0Number(i32),
255+ Text(String)$0,
256+ }"# ,
257+ r#"enum Value {
258+ Unit(()),
259+ Number(i32),
260+ Text(String),
261+ }
262+
263+ impl Value {
264+ fn try_into_number(self) -> Result<i32, Self> {
265+ if let Self::Number(v) = self {
266+ Ok(v)
267+ } else {
268+ Err(self)
269+ }
270+ }
271+
272+ fn try_into_text(self) -> Result<String, Self> {
273+ if let Self::Text(v) = self {
274+ Ok(v)
275+ } else {
276+ Err(self)
277+ }
278+ }
279+ }"# ,
280+ ) ;
281+ }
282+
219283 #[ test]
220284 fn test_generate_enum_try_into_already_implemented ( ) {
221285 check_assist_not_applicable (
@@ -323,6 +387,42 @@ impl Value {
323387 ) ;
324388 }
325389
390+ #[ test]
391+ fn test_generate_enum_as_multiple_tuple_variant ( ) {
392+ check_assist (
393+ generate_enum_as_method,
394+ r#"
395+ enum Value {
396+ Unit(()),
397+ $0Number(i32),
398+ Text(String)$0,
399+ }"# ,
400+ r#"enum Value {
401+ Unit(()),
402+ Number(i32),
403+ Text(String),
404+ }
405+
406+ impl Value {
407+ fn as_number(&self) -> Option<&i32> {
408+ if let Self::Number(v) = self {
409+ Some(v)
410+ } else {
411+ None
412+ }
413+ }
414+
415+ fn as_text(&self) -> Option<&String> {
416+ if let Self::Text(v) = self {
417+ Some(v)
418+ } else {
419+ None
420+ }
421+ }
422+ }"# ,
423+ ) ;
424+ }
425+
326426 #[ test]
327427 fn test_generate_enum_as_record_variant ( ) {
328428 check_assist (
0 commit comments