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 // fn is_minor(&self) -> bool {
29 // matches!(self, Self::Minor)
33 pub(crate) fn generate_enum_match_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
34 let variant = ctx.find_node_at_offset::<ast::Variant>()?;
35 let variant_name = variant.name()?;
36 let parent_enum = variant.parent_enum();
37 if !matches!(variant.kind(), ast::StructKind::Unit) {
38 mark::hit!(test_gen_enum_match_on_non_unit_variant_not_implemented);
42 let fn_name = to_lower_snake_case(&variant_name.to_string());
44 // Return early if we've found an existing new fn
45 let impl_def = find_struct_impl(
47 &ast::AdtDef::Enum(parent_enum.clone()),
48 format!("is_{}", fn_name).as_str(),
51 let target = variant.syntax().text_range();
53 AssistId("generate_enum_match_method", AssistKind::Generate),
54 "Generate an `is_` method for an enum variant",
57 let mut buf = String::with_capacity(512);
59 if impl_def.is_some() {
63 let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v));
67 " {}fn is_{}(&self) -> bool {{
68 matches!(self, Self::{})
75 let start_offset = impl_def
76 .and_then(|impl_def| {
80 .descendants_with_tokens()
81 .find(|t| t.kind() == T!['{'])?
88 buf = generate_impl_text(&parent_enum, &buf);
89 parent_enum.syntax().text_range().end()
92 builder.insert(start_offset, buf);
97 // Generates the surrounding `impl Type { <code> }` including type and lifetime
99 fn generate_impl_text(strukt: &ast::Enum, code: &str) -> String {
100 let mut buf = String::with_capacity(code.len());
101 buf.push_str("\n\nimpl ");
102 buf.push_str(strukt.name().unwrap().text());
103 format_to!(buf, " {{\n{}\n}}", code);
109 use test_utils::mark;
111 use crate::tests::{check_assist, check_assist_not_applicable};
115 fn check_not_applicable(ra_fixture: &str) {
116 check_assist_not_applicable(generate_enum_match_method, ra_fixture)
120 fn test_generate_enum_match_from_variant() {
122 generate_enum_match_method,
136 fn is_minor(&self) -> bool {
137 matches!(self, Self::Minor)
144 fn test_generate_enum_match_already_implemented() {
145 check_not_applicable(
154 fn is_minor(&self) -> bool {
155 matches!(self, Self::Minor)
162 fn test_add_from_impl_no_element() {
163 mark::check!(test_gen_enum_match_on_non_unit_variant_not_implemented);
164 check_not_applicable(
175 fn test_generate_enum_match_from_variant_with_one_variant() {
177 generate_enum_match_method,
178 r#"enum Variant { Undefi$0ned }"#,
180 enum Variant { Undefined }
183 fn is_undefined(&self) -> bool {
184 matches!(self, Self::Undefined)
191 fn test_generate_enum_match_from_variant_with_visibility_marker() {
193 generate_enum_match_method,
195 pub(crate) enum Variant {
200 r#"pub(crate) enum Variant {
207 pub(crate) fn is_minor(&self) -> bool {
208 matches!(self, Self::Minor)