1 use stdx::{format_to, to_lower_snake_case};
2 use syntax::ast::{self, AstNode, NameOwner};
3 use syntax::{ast::VisibilityOwner, T};
6 use crate::{utils::find_struct_impl, AssistContext, AssistId, AssistKind, Assists};
8 // Assist: generate_enum_match_method
10 // Generate an `is_` method for an enum variant.
28 // /// Returns `true` if the version is [`Minor`].
29 // fn is_minor(&self) -> bool {
30 // matches!(self, Self::Minor)
34 pub(crate) fn generate_enum_match_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
35 let variant = ctx.find_node_at_offset::<ast::Variant>()?;
36 let variant_name = variant.name()?;
37 let parent_enum = variant.parent_enum();
38 if !matches!(variant.kind(), ast::StructKind::Unit) {
39 mark::hit!(test_gen_enum_match_on_non_unit_variant_not_implemented);
43 let enum_lowercase_name = to_lower_snake_case(&parent_enum.name()?.to_string());
44 let fn_name = to_lower_snake_case(&variant_name.to_string());
46 // Return early if we've found an existing new fn
47 let impl_def = find_struct_impl(
49 &ast::AdtDef::Enum(parent_enum.clone()),
50 format!("is_{}", fn_name).as_str(),
53 let target = variant.syntax().text_range();
55 AssistId("generate_enum_match_method", AssistKind::Generate),
56 "Generate an `is_` method for an enum variant",
59 let mut buf = String::with_capacity(512);
61 if impl_def.is_some() {
65 let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v));
69 " /// Returns `true` if the {} is [`{}`].
70 {}fn is_{}(&self) -> bool {{
71 matches!(self, Self::{})
80 let start_offset = impl_def
81 .and_then(|impl_def| {
85 .descendants_with_tokens()
86 .find(|t| t.kind() == T!['{'])?
93 buf = generate_impl_text(&parent_enum, &buf);
94 parent_enum.syntax().text_range().end()
97 builder.insert(start_offset, buf);
102 // Generates the surrounding `impl Type { <code> }` including type and lifetime
104 fn generate_impl_text(strukt: &ast::Enum, code: &str) -> String {
105 let mut buf = String::with_capacity(code.len());
106 buf.push_str("\n\nimpl ");
107 buf.push_str(strukt.name().unwrap().text());
108 format_to!(buf, " {{\n{}\n}}", code);
114 use test_utils::mark;
116 use crate::tests::{check_assist, check_assist_not_applicable};
120 fn check_not_applicable(ra_fixture: &str) {
121 check_assist_not_applicable(generate_enum_match_method, ra_fixture)
125 fn test_generate_enum_match_from_variant() {
127 generate_enum_match_method,
141 /// Returns `true` if the variant is [`Minor`].
142 fn is_minor(&self) -> bool {
143 matches!(self, Self::Minor)
150 fn test_generate_enum_match_already_implemented() {
151 check_not_applicable(
160 fn is_minor(&self) -> bool {
161 matches!(self, Self::Minor)
168 fn test_add_from_impl_no_element() {
169 mark::check!(test_gen_enum_match_on_non_unit_variant_not_implemented);
170 check_not_applicable(
181 fn test_generate_enum_match_from_variant_with_one_variant() {
183 generate_enum_match_method,
184 r#"enum Variant { Undefi$0ned }"#,
186 enum Variant { Undefined }
189 /// Returns `true` if the variant is [`Undefined`].
190 fn is_undefined(&self) -> bool {
191 matches!(self, Self::Undefined)
198 fn test_generate_enum_match_from_variant_with_visibility_marker() {
200 generate_enum_match_method,
202 pub(crate) enum Variant {
207 r#"pub(crate) enum Variant {
214 /// Returns `true` if the variant is [`Minor`].
215 pub(crate) fn is_minor(&self) -> bool {
216 matches!(self, Self::Minor)