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