ide_assists/handlers/
unwrap_tuple.rs1use std::iter;
2
3use either::Either;
4use syntax::{
5 AstNode, T,
6 ast::{self, edit::AstNodeEdit},
7};
8
9use crate::{AssistContext, AssistId, Assists};
10
11pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
29 let let_kw = ctx.find_token_syntax_at_offset(T![let])?;
30 let let_stmt = let_kw.parent().and_then(Either::<ast::LetStmt, ast::LetExpr>::cast)?;
31 let mut indent_level = let_stmt.indent_level();
32 let pat = either::for_both!(&let_stmt, it => it.pat())?;
33 let (ty, init, prefix, suffix) = match &let_stmt {
34 Either::Left(let_stmt) => (let_stmt.ty(), let_stmt.initializer()?, "", ";"),
35 Either::Right(let_expr) => {
36 indent_level += 1;
37 (None, let_expr.expr()?, "&& ", "")
38 }
39 };
40
41 let tuple_pat = match pat {
43 ast::Pat::TuplePat(pat) => pat,
44 _ => return None,
45 };
46 let tuple_ty = ty.and_then(|it| match it {
47 ast::Type::TupleType(ty) => Some(ty),
48 _ => None,
49 });
50 let tuple_init = match init {
51 ast::Expr::TupleExpr(expr) => expr,
52 _ => return None,
53 };
54
55 if tuple_pat.fields().count() != tuple_init.fields().count() {
56 return None;
57 }
58 if let Some(tys) = &tuple_ty
59 && tuple_pat.fields().count() != tys.fields().count()
60 {
61 return None;
62 }
63
64 let parent = let_kw.parent()?;
65
66 acc.add(
67 AssistId::refactor_rewrite("unwrap_tuple"),
68 "Unwrap tuple",
69 let_kw.text_range(),
70 |edit| {
71 let mut decls = String::new();
72
73 let tys =
76 tuple_ty.into_iter().flat_map(|it| it.fields().map(Some)).chain(iter::repeat(None));
77 for (pat, ty, expr) in itertools::izip!(tuple_pat.fields(), tys, tuple_init.fields()) {
78 let ty = ty.map_or_else(String::new, |ty| format!(": {ty}"));
79 decls.push_str(&format!("{prefix}let {pat}{ty} = {expr}{suffix}\n{indent_level}"))
80 }
81
82 let s = decls.trim();
83 edit.replace(parent.text_range(), s.strip_prefix(prefix).unwrap_or(s));
84 },
85 )
86}
87
88#[cfg(test)]
89mod tests {
90 use crate::tests::check_assist;
91
92 use super::*;
93
94 #[test]
95 fn unwrap_tuples() {
96 check_assist(
97 unwrap_tuple,
98 r#"
99fn main() {
100 $0let (foo, bar) = ("Foo", "Bar");
101}
102"#,
103 r#"
104fn main() {
105 let foo = "Foo";
106 let bar = "Bar";
107}
108"#,
109 );
110
111 check_assist(
112 unwrap_tuple,
113 r#"
114fn main() {
115 $0let (foo, bar, baz) = ("Foo", "Bar", "Baz");
116}
117"#,
118 r#"
119fn main() {
120 let foo = "Foo";
121 let bar = "Bar";
122 let baz = "Baz";
123}
124"#,
125 );
126 }
127
128 #[test]
129 fn unwrap_tuples_in_let_expr() {
130 check_assist(
131 unwrap_tuple,
132 r#"
133fn main() {
134 if $0let (foo, bar) = ("Foo", "Bar") {
135 code();
136 }
137}
138"#,
139 r#"
140fn main() {
141 if let foo = "Foo"
142 && let bar = "Bar" {
143 code();
144 }
145}
146"#,
147 );
148 }
149
150 #[test]
151 fn unwrap_tuple_with_types() {
152 check_assist(
153 unwrap_tuple,
154 r#"
155fn main() {
156 $0let (foo, bar): (u8, i32) = (5, 10);
157}
158"#,
159 r#"
160fn main() {
161 let foo: u8 = 5;
162 let bar: i32 = 10;
163}
164"#,
165 );
166
167 check_assist(
168 unwrap_tuple,
169 r#"
170fn main() {
171 $0let (foo, bar, baz): (u8, i32, f64) = (5, 10, 17.5);
172}
173"#,
174 r#"
175fn main() {
176 let foo: u8 = 5;
177 let bar: i32 = 10;
178 let baz: f64 = 17.5;
179}
180"#,
181 );
182 }
183}