boa_ast/statement/
switch.rs1use crate::{
4 StatementList,
5 expression::Expression,
6 operations::{ContainsSymbol, contains},
7 scope::Scope,
8 statement::Statement,
9 visitor::{VisitWith, Visitor, VisitorMut},
10};
11use boa_interner::{Interner, ToIndentedString, ToInternedString};
12use core::{fmt::Write as _, ops::ControlFlow};
13
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
24#[derive(Clone, Debug, PartialEq)]
25pub struct Case {
26 condition: Option<Expression>,
27 body: StatementList,
28}
29
30impl Case {
31 #[inline]
33 #[must_use]
34 pub const fn new(condition: Expression, body: StatementList) -> Self {
35 Self {
36 condition: Some(condition),
37 body,
38 }
39 }
40
41 #[inline]
43 #[must_use]
44 pub const fn default(body: StatementList) -> Self {
45 Self {
46 condition: None,
47 body,
48 }
49 }
50
51 #[inline]
56 #[must_use]
57 pub const fn condition(&self) -> Option<&Expression> {
58 self.condition.as_ref()
59 }
60
61 #[inline]
63 #[must_use]
64 pub const fn body(&self) -> &StatementList {
65 &self.body
66 }
67
68 #[inline]
70 #[must_use]
71 pub const fn is_default(&self) -> bool {
72 self.condition.is_none()
73 }
74}
75
76impl VisitWith for Case {
77 fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
78 where
79 V: Visitor<'a>,
80 {
81 if let Some(condition) = &self.condition {
82 visitor.visit_expression(condition)?;
83 }
84
85 visitor.visit_statement_list(&self.body)
86 }
87
88 fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
89 where
90 V: VisitorMut<'a>,
91 {
92 if let Some(condition) = &mut self.condition {
93 visitor.visit_expression_mut(condition)?;
94 }
95 visitor.visit_statement_list_mut(&mut self.body)
96 }
97}
98
99#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
116#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
117#[derive(Clone, Debug, PartialEq)]
118pub struct Switch {
119 pub(crate) val: Expression,
120 pub(crate) cases: Box<[Case]>,
121 pub(crate) contains_direct_eval: bool,
122
123 #[cfg_attr(feature = "serde", serde(skip))]
124 pub(crate) scope: Option<Scope>,
125}
126
127impl Switch {
128 #[inline]
130 #[must_use]
131 pub fn new(val: Expression, cases: Box<[Case]>) -> Self {
132 let mut contains_direct_eval = false;
133 for case in &cases {
134 contains_direct_eval |= contains(case, ContainsSymbol::DirectEval);
135 }
136 Self {
137 val,
138 cases,
139 contains_direct_eval,
140 scope: None,
141 }
142 }
143
144 #[inline]
146 #[must_use]
147 pub const fn val(&self) -> &Expression {
148 &self.val
149 }
150
151 #[inline]
153 #[must_use]
154 pub const fn cases(&self) -> &[Case] {
155 &self.cases
156 }
157
158 #[inline]
160 #[must_use]
161 pub fn default(&self) -> Option<&StatementList> {
162 for case in self.cases.as_ref() {
163 if case.is_default() {
164 return Some(case.body());
165 }
166 }
167 None
168 }
169
170 #[inline]
172 #[must_use]
173 pub const fn scope(&self) -> Option<&Scope> {
174 self.scope.as_ref()
175 }
176}
177
178impl ToIndentedString for Switch {
179 fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
180 let indent = " ".repeat(indentation);
181 let mut buf = format!("switch ({}) {{\n", self.val().to_interned_string(interner));
182 for e in &*self.cases {
183 if let Some(condition) = e.condition() {
184 let _ = write!(
185 buf,
186 "{indent} case {}:\n{}",
187 condition.to_interned_string(interner),
188 e.body().to_indented_string(interner, indentation + 2)
189 );
190 } else {
191 let _ = write!(
192 buf,
193 "{indent} default:\n{}",
194 e.body().to_indented_string(interner, indentation + 2)
195 );
196 }
197 }
198
199 let _ = write!(buf, "{indent}}}");
200
201 buf
202 }
203}
204
205impl From<Switch> for Statement {
206 #[inline]
207 fn from(switch: Switch) -> Self {
208 Self::Switch(switch)
209 }
210}
211
212impl VisitWith for Switch {
213 fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
214 where
215 V: Visitor<'a>,
216 {
217 visitor.visit_expression(&self.val)?;
218 for case in &*self.cases {
219 visitor.visit_case(case)?;
220 }
221 ControlFlow::Continue(())
222 }
223
224 fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
225 where
226 V: VisitorMut<'a>,
227 {
228 visitor.visit_expression_mut(&mut self.val)?;
229 for case in &mut *self.cases {
230 visitor.visit_case_mut(case)?;
231 }
232 ControlFlow::Continue(())
233 }
234}