ide_assists/handlers/
unwrap_tuple.rs1use syntax::{
2 AstNode, T,
3 ast::{self, edit::AstNodeEdit},
4};
5
6use crate::{AssistContext, AssistId, Assists};
7
8pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
26 let let_kw = ctx.find_token_syntax_at_offset(T![let])?;
27 let let_stmt = let_kw.parent().and_then(ast::LetStmt::cast)?;
28 let indent_level = let_stmt.indent_level().0 as usize;
29 let pat = let_stmt.pat()?;
30 let ty = let_stmt.ty();
31 let init = let_stmt.initializer()?;
32
33 let tuple_pat = match pat {
35 ast::Pat::TuplePat(pat) => pat,
36 _ => return None,
37 };
38 let tuple_ty = ty.and_then(|it| match it {
39 ast::Type::TupleType(ty) => Some(ty),
40 _ => None,
41 });
42 let tuple_init = match init {
43 ast::Expr::TupleExpr(expr) => expr,
44 _ => return None,
45 };
46
47 if tuple_pat.fields().count() != tuple_init.fields().count() {
48 return None;
49 }
50 if let Some(tys) = &tuple_ty
51 && tuple_pat.fields().count() != tys.fields().count()
52 {
53 return None;
54 }
55
56 let parent = let_kw.parent()?;
57
58 acc.add(
59 AssistId::refactor_rewrite("unwrap_tuple"),
60 "Unwrap tuple",
61 let_kw.text_range(),
62 |edit| {
63 let indents = " ".repeat(indent_level);
64
65 if let Some(tys) = tuple_ty {
68 let mut zipped_decls = String::new();
69 for (pat, ty, expr) in
70 itertools::izip!(tuple_pat.fields(), tys.fields(), tuple_init.fields())
71 {
72 zipped_decls.push_str(&format!("{indents}let {pat}: {ty} = {expr};\n"))
73 }
74 edit.replace(parent.text_range(), zipped_decls.trim());
75 } else {
76 let mut zipped_decls = String::new();
77 for (pat, expr) in itertools::izip!(tuple_pat.fields(), tuple_init.fields()) {
78 zipped_decls.push_str(&format!("{indents}let {pat} = {expr};\n"));
79 }
80 edit.replace(parent.text_range(), zipped_decls.trim());
81 }
82 },
83 )
84}
85
86#[cfg(test)]
87mod tests {
88 use crate::tests::check_assist;
89
90 use super::*;
91
92 #[test]
93 fn unwrap_tuples() {
94 check_assist(
95 unwrap_tuple,
96 r#"
97fn main() {
98 $0let (foo, bar) = ("Foo", "Bar");
99}
100"#,
101 r#"
102fn main() {
103 let foo = "Foo";
104 let bar = "Bar";
105}
106"#,
107 );
108
109 check_assist(
110 unwrap_tuple,
111 r#"
112fn main() {
113 $0let (foo, bar, baz) = ("Foo", "Bar", "Baz");
114}
115"#,
116 r#"
117fn main() {
118 let foo = "Foo";
119 let bar = "Bar";
120 let baz = "Baz";
121}
122"#,
123 );
124 }
125
126 #[test]
127 fn unwrap_tuple_with_types() {
128 check_assist(
129 unwrap_tuple,
130 r#"
131fn main() {
132 $0let (foo, bar): (u8, i32) = (5, 10);
133}
134"#,
135 r#"
136fn main() {
137 let foo: u8 = 5;
138 let bar: i32 = 10;
139}
140"#,
141 );
142
143 check_assist(
144 unwrap_tuple,
145 r#"
146fn main() {
147 $0let (foo, bar, baz): (u8, i32, f64) = (5, 10, 17.5);
148}
149"#,
150 r#"
151fn main() {
152 let foo: u8 = 5;
153 let bar: i32 = 10;
154 let baz: f64 = 17.5;
155}
156"#,
157 );
158 }
159}