Skip to main content

rustidy_ast/expr/with_block/
match_.rs

1//! Match expression
2
3// Imports
4use {
5	crate::{
6		attr::{self, BracedWithInnerAttributes, WithOuterAttributes},
7		expr::{Expression, ExpressionInner, ExpressionWithBlock, ExpressionWithoutBlock},
8		pat::Pattern,
9		token,
10	},
11	super::Conditions,
12	core::ops::ControlFlow,
13	rustidy_format::{Format, Formattable, WhitespaceFormat},
14	rustidy_parse::{FromRecursiveRoot, Parse, ParseError, Parser, ParserError, ParserTag},
15	rustidy_print::Print,
16	rustidy_util::Whitespace,
17};
18
19/// `MatchExpression`
20#[derive(PartialEq, Eq, Clone, Debug)]
21#[derive(serde::Serialize, serde::Deserialize)]
22#[derive(Parse, Formattable, Format, Print)]
23#[parse(name = "a match expression")]
24pub struct MatchExpression {
25	pub match_:    token::Match,
26	#[parse(fatal)]
27	#[format(prefix_ws = Whitespace::SINGLE)]
28	pub scrutinee: Box<Scrutinee>,
29	#[format(prefix_ws = Whitespace::SINGLE)]
30	pub arms:      BracedWithInnerAttributes<Option<MatchArms>>,
31}
32
33/// `Scrutinee`
34#[derive(PartialEq, Eq, Clone, Debug)]
35#[derive(serde::Serialize, serde::Deserialize)]
36#[derive(Parse, Formattable, Format, Print)]
37pub struct Scrutinee(#[parse(with_tag = ParserTag::SkipStructExpression)] Expression);
38
39/// `MatchArms`
40#[derive(PartialEq, Eq, Clone, Debug)]
41#[derive(serde::Serialize, serde::Deserialize)]
42#[derive(Formattable, Format, Print)]
43pub struct MatchArms {
44	#[format(args = rustidy_format::vec::args_prefix_ws(Whitespace::INDENT))]
45	pub arms: Vec<MatchArmWithExpr>,
46}
47
48impl Parse for MatchArms {
49	type Error = MatchArmsError;
50
51	fn parse_from(parser: &mut Parser) -> Result<Self, Self::Error> {
52		let mut arms = vec![];
53		loop {
54			// TODO: For some reason, clippy (and only clippy) errors out when we
55			//       merge this into `let Ok(arm) = ... else { break }`.
56			let arm_res = parser.try_parse::<MatchArm>()?;
57			let Ok(arm) = arm_res else {
58				break
59			};
60			let arrow = parser.parse::<token::FatArrow>()?;
61
62
63			// Note: Because of `match () { () => {} () => {} }` and `match () { () => {}?, () => {} }`
64			//       requiring look-ahead after the block expressions, we need to parse both with and
65			//       without block.
66			let with_block_res = parser
67				.peek::<(ExpressionWithBlock, Option<token::Comma>)>()?;
68			let without_block_res = parser
69				.peek::<(ExpressionWithoutBlock, Option<token::Comma>)>()?;
70			let (expr, trailing_comma, control_flow) = match (with_block_res, without_block_res) {
71				// If both parse, we only accept with block if the without block had no comma.
72				(Ok(((expr_with_block, with_block_trailing_comma), with_block_peek_state)), Ok(((expr_without_block, without_block_trailing_comma), without_block_peek_state)),) => match without_block_trailing_comma {
73					Some(trailing_comma) => {
74						parser.set_peeked(without_block_peek_state);
75
76						let expr = Expression::from_recursive_root(
77							ExpressionInner::from(expr_without_block),
78							parser
79						);
80						(expr, Some(trailing_comma), ControlFlow::Continue(()))
81					},
82					None => {
83						parser.set_peeked(with_block_peek_state);
84
85						let expr = Expression::from_recursive_root(ExpressionInner::from(expr_with_block), parser);
86						(expr, with_block_trailing_comma, ControlFlow::Continue(()))
87					},
88				},
89				// If only one of them parses, we take it
90				(Ok(((expr, trailing_comma), peek_state)), Err(_)) => {
91					parser.set_peeked(peek_state);
92
93					let expr = Expression::from_recursive_root(ExpressionInner::from(expr), parser);
94					(expr, trailing_comma, ControlFlow::Continue(()))
95				},
96				(Err(_), Ok(((expr, trailing_comma), peek_state))) => {
97					parser.set_peeked(peek_state);
98
99					let expr = Expression::from_recursive_root(ExpressionInner::from(expr), parser);
100					let control_flow = match trailing_comma.is_some() {
101						true => ControlFlow::Continue(()),
102						false => ControlFlow::Break(()),
103					};
104
105					(expr, trailing_comma, control_flow)
106				},
107				(Err(with_block), Err(without_block)) => return Err(
108					Self::Error::Expression { with_block, without_block, }
109				),
110			};
111
112			arms.push(
113				MatchArmWithExpr { arm, arrow, expr, trailing_comma, }
114			);
115
116			if control_flow.is_break() {
117				break;
118			}
119		}
120
121		Ok(Self { arms })
122	}
123}
124
125#[derive(derive_more::Debug, derive_more::From, ParseError)]
126pub enum MatchArmsError {
127	#[parse_error(transparent)]
128	MatchArm(ParserError<MatchArm>),
129
130	#[parse_error(transparent)]
131	#[parse_error(fatal)]
132	FatArrow(ParserError<token::FatArrow>),
133
134	#[parse_error(transparent)]
135	#[parse_error(fatal)]
136	ExpressionWithBlock(ParserError<(ExpressionWithBlock, Option<token::Comma>)>),
137
138	#[parse_error(transparent)]
139	#[parse_error(fatal)]
140	ExpressionWithoutBlock(ParserError<(ExpressionWithoutBlock, Option<token::Comma>)>),
141
142	#[parse_error(multiple)]
143	#[parse_error(fatal)]
144	Expression {
145		with_block:    ParserError<(ExpressionWithBlock, Option<token::Comma>)>,
146		without_block: ParserError<(ExpressionWithoutBlock, Option<token::Comma>)>,
147	},
148
149	#[parse_error(transparent)]
150	#[parse_error(fatal)]
151	Comma(ParserError<token::Comma>),
152}
153
154#[derive(PartialEq, Eq, Clone, Debug)]
155#[derive(serde::Serialize, serde::Deserialize)]
156#[derive(Parse, Formattable, Format, Print)]
157pub struct MatchArmWithExpr {
158	pub arm:            MatchArm,
159	#[format(prefix_ws = Whitespace::SINGLE)]
160	pub arrow:          token::FatArrow,
161	#[format(prefix_ws = Whitespace::SINGLE)]
162	pub expr:           Expression,
163	#[format(prefix_ws = Whitespace::REMOVE)]
164	pub trailing_comma: Option<token::Comma>,
165}
166
167/// `MatchArm`
168#[derive(PartialEq, Eq, Clone, Debug)]
169#[derive(serde::Serialize, serde::Deserialize)]
170#[derive(Parse, Formattable, Format, Print)]
171pub struct MatchArm(
172	#[format(args = attr::with::fmt(Whitespace::INDENT))]
173	pub WithOuterAttributes<MatchArmInner>,
174);
175
176#[derive(PartialEq, Eq, Clone, Debug)]
177#[derive(serde::Serialize, serde::Deserialize)]
178#[derive(Parse, Formattable, Format, Print)]
179#[parse(name = "a match arm")]
180pub struct MatchArmInner {
181	pub pat:   Pattern,
182	#[format(prefix_ws = Whitespace::SINGLE)]
183	pub guard: Option<MatchArmGuard>,
184}
185
186/// `MatchArmGuard`
187#[derive(PartialEq, Eq, Clone, Debug)]
188#[derive(serde::Serialize, serde::Deserialize)]
189#[derive(Parse, Formattable, Format, Print)]
190pub struct MatchArmGuard {
191	pub if_:  token::If,
192	// TODO: The reference says this is just an expression, but
193	//       that means we don't parse `Some(...) if let ...`, so
194	//       instead we allow any conditions.
195	#[format(prefix_ws = Whitespace::SINGLE)]
196	pub cond: Conditions,
197}