Skip to content

Commit 4ab285a

Browse files
committed
make generate_enum_is_method work on any variants
1 parent f675860 commit 4ab285a

File tree

1 file changed

+95
-18
lines changed

1 file changed

+95
-18
lines changed

crates/ide_assists/src/handlers/generate_enum_match_method.rs

Lines changed: 95 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use stdx::{format_to, to_lower_snake_case};
22
use syntax::ast::VisibilityOwner;
33
use syntax::ast::{self, AstNode, NameOwner};
4-
use test_utils::mark;
54

65
use crate::{
76
utils::{find_impl_block_end, find_struct_impl, generate_impl_text},
@@ -37,20 +36,17 @@ use crate::{
3736
pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
3837
let variant = ctx.find_node_at_offset::<ast::Variant>()?;
3938
let variant_name = variant.name()?;
40-
let parent_enum = variant.parent_enum();
41-
if !matches!(variant.kind(), ast::StructKind::Unit) {
42-
mark::hit!(test_gen_enum_match_on_non_unit_variant_not_implemented);
43-
return None;
44-
}
39+
let parent_enum = ast::Adt::Enum(variant.parent_enum());
40+
let variant_kind = variant_kind(&variant);
4541

4642
let enum_lowercase_name = to_lower_snake_case(&parent_enum.name()?.to_string());
47-
let fn_name = to_lower_snake_case(&variant_name.to_string());
43+
let fn_name = format!("is_{}", &to_lower_snake_case(variant_name.text()));
4844

4945
// Return early if we've found an existing new fn
5046
let impl_def = find_struct_impl(
5147
&ctx,
52-
&ast::Adt::Enum(parent_enum.clone()),
53-
format!("is_{}", fn_name).as_str(),
48+
&parent_enum,
49+
&fn_name,
5450
)?;
5551

5652
let target = variant.syntax().text_range();
@@ -69,20 +65,21 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) ->
6965
format_to!(
7066
buf,
7167
" /// Returns `true` if the {} is [`{}`].
72-
{}fn is_{}(&self) -> bool {{
73-
matches!(self, Self::{})
68+
{}fn {}(&self) -> bool {{
69+
matches!(self, Self::{}{})
7470
}}",
7571
enum_lowercase_name,
7672
variant_name,
7773
vis,
7874
fn_name,
79-
variant_name
75+
variant_name,
76+
variant_kind.pattern_suffix(),
8077
);
8178

8279
let start_offset = impl_def
8380
.and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
8481
.unwrap_or_else(|| {
85-
buf = generate_impl_text(&ast::Adt::Enum(parent_enum.clone()), &buf);
82+
buf = generate_impl_text(&parent_enum, &buf);
8683
parent_enum.syntax().text_range().end()
8784
});
8885

@@ -91,10 +88,53 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) ->
9188
)
9289
}
9390

91+
enum VariantKind {
92+
Unit,
93+
/// Tuple with a single field
94+
NewtypeTuple,
95+
/// Tuple with 0 or more than 2 fields
96+
Tuple,
97+
/// Record with a single field
98+
NewtypeRecord { field_name: Option<ast::Name> },
99+
/// Record with 0 or more than 2 fields
100+
Record,
101+
}
102+
103+
impl VariantKind {
104+
fn pattern_suffix(&self) -> &'static str {
105+
match self {
106+
VariantKind::Unit => "",
107+
VariantKind::NewtypeTuple |
108+
VariantKind::Tuple => "(..)",
109+
VariantKind::NewtypeRecord { .. } |
110+
VariantKind::Record => " { .. }",
111+
}
112+
}
113+
}
114+
115+
fn variant_kind(variant: &ast::Variant) -> VariantKind {
116+
match variant.kind() {
117+
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 }
121+
} else {
122+
VariantKind::Record
123+
}
124+
}
125+
ast::StructKind::Tuple(tuple) => {
126+
if tuple.fields().count() == 1 {
127+
VariantKind::NewtypeTuple
128+
} else {
129+
VariantKind::Tuple
130+
}
131+
}
132+
ast::StructKind::Unit => VariantKind::Unit,
133+
}
134+
}
135+
94136
#[cfg(test)]
95137
mod tests {
96-
use test_utils::mark;
97-
98138
use crate::tests::{check_assist, check_assist_not_applicable};
99139

100140
use super::*;
@@ -147,14 +187,51 @@ impl Variant {
147187
}
148188

149189
#[test]
150-
fn test_add_from_impl_no_element() {
151-
mark::check!(test_gen_enum_match_on_non_unit_variant_not_implemented);
152-
check_not_applicable(
190+
fn test_generate_enum_match_from_tuple_variant() {
191+
check_assist(
192+
generate_enum_is_method,
153193
r#"
154194
enum Variant {
155195
Undefined,
156196
Minor(u32)$0,
157197
Major,
198+
}"#,
199+
r#"enum Variant {
200+
Undefined,
201+
Minor(u32),
202+
Major,
203+
}
204+
205+
impl Variant {
206+
/// Returns `true` if the variant is [`Minor`].
207+
fn is_minor(&self) -> bool {
208+
matches!(self, Self::Minor(..))
209+
}
210+
}"#,
211+
);
212+
}
213+
214+
#[test]
215+
fn test_generate_enum_match_from_record_variant() {
216+
check_assist(
217+
generate_enum_is_method,
218+
r#"
219+
enum Variant {
220+
Undefined,
221+
Minor { foo: i32 }$0,
222+
Major,
223+
}"#,
224+
r#"enum Variant {
225+
Undefined,
226+
Minor { foo: i32 },
227+
Major,
228+
}
229+
230+
impl Variant {
231+
/// Returns `true` if the variant is [`Minor`].
232+
fn is_minor(&self) -> bool {
233+
matches!(self, Self::Minor { .. })
234+
}
158235
}"#,
159236
);
160237
}

0 commit comments

Comments
 (0)