mago_syntax/cst/cst/control_flow/
switch.rs1use strum::Display;
2
3use mago_span::HasSpan;
4use mago_span::Span;
5
6use crate::cst::cst::expression::Expression;
7use crate::cst::cst::keyword::Keyword;
8use crate::cst::cst::statement::Statement;
9use crate::cst::cst::terminator::Terminator;
10use crate::cst::sequence::Sequence;
11
12#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize))]
15pub struct Switch<'arena> {
16 pub switch: Keyword<'arena>,
17 pub left_parenthesis: Span,
18 pub expression: &'arena Expression<'arena>,
19 pub right_parenthesis: Span,
20 pub body: SwitchBody<'arena>,
21}
22
23#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord, Display)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize))]
26#[cfg_attr(feature = "serde", serde(tag = "type", content = "value"))]
27pub enum SwitchBody<'arena> {
28 BraceDelimited(SwitchBraceDelimitedBody<'arena>),
29 ColonDelimited(SwitchColonDelimitedBody<'arena>),
30}
31
32#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
34#[cfg_attr(feature = "serde", derive(serde::Serialize))]
35pub struct SwitchBraceDelimitedBody<'arena> {
36 pub left_brace: Span,
37 pub optional_terminator: Option<Terminator<'arena>>,
38 pub cases: Sequence<'arena, SwitchCase<'arena>>,
39 pub right_brace: Span,
40}
41
42#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
44#[cfg_attr(feature = "serde", derive(serde::Serialize))]
45pub struct SwitchColonDelimitedBody<'arena> {
46 pub colon: Span,
47 pub optional_terminator: Option<Terminator<'arena>>,
48 pub cases: Sequence<'arena, SwitchCase<'arena>>,
49 pub end_switch: Keyword<'arena>,
50 pub terminator: Terminator<'arena>,
51}
52
53#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord, Display)]
55#[cfg_attr(feature = "serde", derive(serde::Serialize))]
56#[cfg_attr(feature = "serde", serde(tag = "type", content = "value"))]
57pub enum SwitchCase<'arena> {
58 Expression(SwitchExpressionCase<'arena>),
59 Default(SwitchDefaultCase<'arena>),
60}
61
62#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
66#[cfg_attr(feature = "serde", derive(serde::Serialize))]
67pub struct SwitchExpressionCase<'arena> {
68 pub case: Keyword<'arena>,
69 pub expression: &'arena Expression<'arena>,
70 pub separator: SwitchCaseSeparator,
71 pub statements: Sequence<'arena, Statement<'arena>>,
72}
73
74#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
78#[cfg_attr(feature = "serde", derive(serde::Serialize))]
79pub struct SwitchDefaultCase<'arena> {
80 pub default: Keyword<'arena>,
81 pub separator: SwitchCaseSeparator,
82 pub statements: Sequence<'arena, Statement<'arena>>,
83}
84
85#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord, Display)]
87#[cfg_attr(feature = "serde", derive(serde::Serialize))]
88#[cfg_attr(feature = "serde", serde(tag = "type", content = "value"))]
89pub enum SwitchCaseSeparator {
90 Colon(Span),
91 SemiColon(Span),
92}
93
94impl<'arena> SwitchBody<'arena> {
95 pub fn has_default_case(&self) -> bool {
96 self.cases().iter().any(SwitchCase::is_default)
97 }
98
99 #[must_use]
100 pub fn cases(&self) -> &[SwitchCase<'arena>] {
101 match self {
102 SwitchBody::BraceDelimited(body) => body.cases.as_slice(),
103 SwitchBody::ColonDelimited(body) => body.cases.as_slice(),
104 }
105 }
106}
107
108impl<'arena> SwitchCase<'arena> {
109 #[must_use]
111 pub fn expression(&self) -> Option<&Expression<'arena>> {
112 match self {
113 SwitchCase::Expression(case) => Some(case.expression),
114 SwitchCase::Default(_) => None,
115 }
116 }
117
118 #[must_use]
120 pub fn statements(&self) -> &[Statement<'arena>] {
121 match self {
122 SwitchCase::Expression(case) => case.statements.as_slice(),
123 SwitchCase::Default(case) => case.statements.as_slice(),
124 }
125 }
126
127 #[must_use]
129 pub fn is_default(&self) -> bool {
130 match self {
131 SwitchCase::Expression(_) => false,
132 SwitchCase::Default(_) => true,
133 }
134 }
135
136 #[must_use]
138 pub fn is_empty(&self) -> bool {
139 match self {
140 SwitchCase::Expression(case) => case.statements.is_empty(),
141 SwitchCase::Default(case) => case.statements.is_empty(),
142 }
143 }
144
145 #[must_use]
147 pub fn separator(&self) -> &SwitchCaseSeparator {
148 match self {
149 SwitchCase::Expression(case) => &case.separator,
150 SwitchCase::Default(case) => &case.separator,
151 }
152 }
153
154 #[must_use]
159 pub fn is_fall_through(&self) -> bool {
160 let Some(last_statement) = self.statements().last() else {
161 return false;
162 };
163
164 !matches!(last_statement, Statement::Break(_))
165 }
166}
167
168impl HasSpan for Switch<'_> {
169 fn span(&self) -> Span {
170 Span::between(self.switch.span(), self.body.span())
171 }
172}
173
174impl HasSpan for SwitchBody<'_> {
175 fn span(&self) -> Span {
176 match self {
177 SwitchBody::BraceDelimited(body) => body.span(),
178 SwitchBody::ColonDelimited(body) => body.span(),
179 }
180 }
181}
182
183impl HasSpan for SwitchBraceDelimitedBody<'_> {
184 fn span(&self) -> Span {
185 Span::between(self.left_brace, self.right_brace)
186 }
187}
188
189impl HasSpan for SwitchColonDelimitedBody<'_> {
190 fn span(&self) -> Span {
191 Span::between(self.colon, self.terminator.span())
192 }
193}
194
195impl HasSpan for SwitchCase<'_> {
196 fn span(&self) -> Span {
197 match self {
198 SwitchCase::Expression(case) => case.span(),
199 SwitchCase::Default(case) => case.span(),
200 }
201 }
202}
203
204impl HasSpan for SwitchExpressionCase<'_> {
205 fn span(&self) -> Span {
206 Span::between(self.case.span(), self.statements.last().map_or(self.separator.span(), HasSpan::span))
207 }
208}
209
210impl HasSpan for SwitchDefaultCase<'_> {
211 fn span(&self) -> Span {
212 Span::between(self.default.span(), self.statements.last().map_or(self.separator.span(), HasSpan::span))
213 }
214}
215
216impl HasSpan for SwitchCaseSeparator {
217 fn span(&self) -> Span {
218 match self {
219 SwitchCaseSeparator::Colon(span) => *span,
220 SwitchCaseSeparator::SemiColon(span) => *span,
221 }
222 }
223}