1use serde::de::Error as _;
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3
4use crate::common::BaseNode;
5
6use crate::expressions::{Expression, Identifier};
7use crate::patterns::PatternLike;
8
9fn is_false(v: &bool) -> bool {
10 !v
11}
12
13#[derive(Debug, Clone, Serialize)]
14#[serde(tag = "type")]
15pub enum Statement {
16 BlockStatement(BlockStatement),
18 ReturnStatement(ReturnStatement),
19 IfStatement(IfStatement),
20 ForStatement(ForStatement),
21 WhileStatement(WhileStatement),
22 DoWhileStatement(DoWhileStatement),
23 ForInStatement(ForInStatement),
24 ForOfStatement(ForOfStatement),
25 SwitchStatement(SwitchStatement),
26 ThrowStatement(ThrowStatement),
27 TryStatement(TryStatement),
28 BreakStatement(BreakStatement),
29 ContinueStatement(ContinueStatement),
30 LabeledStatement(LabeledStatement),
31 ExpressionStatement(ExpressionStatement),
32 EmptyStatement(EmptyStatement),
33 DebuggerStatement(DebuggerStatement),
34 WithStatement(WithStatement),
35 VariableDeclaration(VariableDeclaration),
37 FunctionDeclaration(FunctionDeclaration),
38 ClassDeclaration(ClassDeclaration),
39 ImportDeclaration(crate::declarations::ImportDeclaration),
41 ExportNamedDeclaration(crate::declarations::ExportNamedDeclaration),
42 ExportDefaultDeclaration(crate::declarations::ExportDefaultDeclaration),
43 ExportAllDeclaration(crate::declarations::ExportAllDeclaration),
44 TSTypeAliasDeclaration(crate::declarations::TSTypeAliasDeclaration),
46 TSInterfaceDeclaration(crate::declarations::TSInterfaceDeclaration),
47 TSEnumDeclaration(crate::declarations::TSEnumDeclaration),
48 TSModuleDeclaration(crate::declarations::TSModuleDeclaration),
49 TSDeclareFunction(crate::declarations::TSDeclareFunction),
50 TypeAlias(crate::declarations::TypeAlias),
52 OpaqueType(crate::declarations::OpaqueType),
53 InterfaceDeclaration(crate::declarations::InterfaceDeclaration),
54 DeclareVariable(crate::declarations::DeclareVariable),
55 DeclareFunction(crate::declarations::DeclareFunction),
56 DeclareClass(crate::declarations::DeclareClass),
57 DeclareModule(crate::declarations::DeclareModule),
58 DeclareModuleExports(crate::declarations::DeclareModuleExports),
59 DeclareExportDeclaration(crate::declarations::DeclareExportDeclaration),
60 DeclareExportAllDeclaration(crate::declarations::DeclareExportAllDeclaration),
61 DeclareInterface(crate::declarations::DeclareInterface),
62 DeclareTypeAlias(crate::declarations::DeclareTypeAlias),
63 DeclareOpaqueType(crate::declarations::DeclareOpaqueType),
64 EnumDeclaration(crate::declarations::EnumDeclaration),
65 #[serde(untagged)]
80 Unknown(UnknownStatement),
81}
82
83#[derive(Debug, Clone)]
88pub struct UnknownStatement {
89 raw: serde_json::Value,
90 base: BaseNode,
91}
92
93impl UnknownStatement {
94 pub fn from_raw(raw: serde_json::Value) -> Result<Self, String> {
95 match raw.get("type").and_then(serde_json::Value::as_str) {
96 Some(_) => {
97 let base = BaseNode::deserialize(&raw)
100 .map_err(|err| format!("failed to read unknown statement base: {err}"))?;
101 Ok(Self { raw, base })
102 }
103 None => Err("unknown statement is missing a string `type` field".to_string()),
104 }
105 }
106
107 pub fn node_type(&self) -> &str {
111 self.base.node_type.as_deref().unwrap_or("Unknown")
112 }
113
114 pub fn raw(&self) -> &serde_json::Value {
115 &self.raw
116 }
117
118 pub fn with_raw_mut<R>(
122 &mut self,
123 f: impl FnOnce(&mut serde_json::Value) -> R,
124 ) -> Result<R, String> {
125 let saved = self.raw.clone();
126 let result = f(&mut self.raw);
127 if self.raw.get("type").and_then(serde_json::Value::as_str).is_none() {
128 self.raw = saved;
129 return Err("unknown statement mutation removed the string `type` field".to_string());
130 }
131 match BaseNode::deserialize(&self.raw) {
132 Ok(base) => {
133 self.base = base;
134 Ok(result)
135 }
136 Err(err) => {
137 self.raw = saved;
138 Err(format!("failed to refresh unknown statement base: {err}"))
139 }
140 }
141 }
142
143 pub fn base(&self) -> &BaseNode {
144 &self.base
145 }
146}
147
148impl Serialize for UnknownStatement {
149 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
150 where
151 S: Serializer,
152 {
153 self.raw.serialize(serializer)
154 }
155}
156
157impl<'de> Deserialize<'de> for UnknownStatement {
158 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
159 where
160 D: Deserializer<'de>,
161 {
162 let raw = serde_json::Value::deserialize(deserializer)?;
163 Self::from_raw(raw).map_err(D::Error::custom)
164 }
165}
166
167impl<'de> Deserialize<'de> for Statement {
168 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
169 where
170 D: Deserializer<'de>,
171 {
172 let raw = serde_json::Value::deserialize(deserializer)?;
173 let node_type = raw
174 .get("type")
175 .and_then(serde_json::Value::as_str)
176 .ok_or_else(|| D::Error::custom("statement is missing a string `type` field"))?
177 .to_string();
178
179 if is_known_statement_type(&node_type) {
180 let known: KnownStatement = serde_json::from_value(raw).map_err(D::Error::custom)?;
181 Ok(known.into())
182 } else {
183 UnknownStatement::from_raw(raw)
184 .map(Statement::Unknown)
185 .map_err(D::Error::custom)
186 }
187 }
188}
189
190macro_rules! known_statements {
197 ($($variant:ident => $ty:ty),+ $(,)?) => {
198 const KNOWN_STATEMENT_TYPES: &[&str] = &[$(stringify!($variant)),+];
199
200 pub fn is_known_statement_type(node_type: &str) -> bool {
208 KNOWN_STATEMENT_TYPES.contains(&node_type)
209 }
210
211 #[derive(Debug, Deserialize)]
212 #[serde(tag = "type")]
213 enum KnownStatement {
214 $($variant($ty),)+
215 }
216
217 impl From<KnownStatement> for Statement {
218 fn from(value: KnownStatement) -> Self {
219 match value {
220 $(KnownStatement::$variant(s) => Statement::$variant(s),)+
221 }
222 }
223 }
224 };
225}
226
227known_statements! {
228 BlockStatement => BlockStatement,
229 ReturnStatement => ReturnStatement,
230 IfStatement => IfStatement,
231 ForStatement => ForStatement,
232 WhileStatement => WhileStatement,
233 DoWhileStatement => DoWhileStatement,
234 ForInStatement => ForInStatement,
235 ForOfStatement => ForOfStatement,
236 SwitchStatement => SwitchStatement,
237 ThrowStatement => ThrowStatement,
238 TryStatement => TryStatement,
239 BreakStatement => BreakStatement,
240 ContinueStatement => ContinueStatement,
241 LabeledStatement => LabeledStatement,
242 ExpressionStatement => ExpressionStatement,
243 EmptyStatement => EmptyStatement,
244 DebuggerStatement => DebuggerStatement,
245 WithStatement => WithStatement,
246 VariableDeclaration => VariableDeclaration,
247 FunctionDeclaration => FunctionDeclaration,
248 ClassDeclaration => ClassDeclaration,
249 ImportDeclaration => crate::declarations::ImportDeclaration,
250 ExportNamedDeclaration => crate::declarations::ExportNamedDeclaration,
251 ExportDefaultDeclaration => crate::declarations::ExportDefaultDeclaration,
252 ExportAllDeclaration => crate::declarations::ExportAllDeclaration,
253 TSTypeAliasDeclaration => crate::declarations::TSTypeAliasDeclaration,
254 TSInterfaceDeclaration => crate::declarations::TSInterfaceDeclaration,
255 TSEnumDeclaration => crate::declarations::TSEnumDeclaration,
256 TSModuleDeclaration => crate::declarations::TSModuleDeclaration,
257 TSDeclareFunction => crate::declarations::TSDeclareFunction,
258 TypeAlias => crate::declarations::TypeAlias,
259 OpaqueType => crate::declarations::OpaqueType,
260 InterfaceDeclaration => crate::declarations::InterfaceDeclaration,
261 DeclareVariable => crate::declarations::DeclareVariable,
262 DeclareFunction => crate::declarations::DeclareFunction,
263 DeclareClass => crate::declarations::DeclareClass,
264 DeclareModule => crate::declarations::DeclareModule,
265 DeclareModuleExports => crate::declarations::DeclareModuleExports,
266 DeclareExportDeclaration => crate::declarations::DeclareExportDeclaration,
267 DeclareExportAllDeclaration => crate::declarations::DeclareExportAllDeclaration,
268 DeclareInterface => crate::declarations::DeclareInterface,
269 DeclareTypeAlias => crate::declarations::DeclareTypeAlias,
270 DeclareOpaqueType => crate::declarations::DeclareOpaqueType,
271 EnumDeclaration => crate::declarations::EnumDeclaration,
272}
273
274#[derive(Debug, Clone, Serialize, Deserialize)]
275pub struct BlockStatement {
276 #[serde(flatten)]
277 pub base: BaseNode,
278 pub body: Vec<Statement>,
279 #[serde(default)]
280 pub directives: Vec<Directive>,
281}
282
283#[derive(Debug, Clone, Serialize, Deserialize)]
284pub struct Directive {
285 #[serde(flatten)]
286 pub base: BaseNode,
287 pub value: DirectiveLiteral,
288}
289
290#[derive(Debug, Clone, Serialize, Deserialize)]
291pub struct DirectiveLiteral {
292 #[serde(flatten)]
293 pub base: BaseNode,
294 pub value: String,
295}
296
297#[derive(Debug, Clone, Serialize, Deserialize)]
298pub struct ReturnStatement {
299 #[serde(flatten)]
300 pub base: BaseNode,
301 pub argument: Option<Box<Expression>>,
302}
303
304#[derive(Debug, Clone, Serialize, Deserialize)]
305pub struct ExpressionStatement {
306 #[serde(flatten)]
307 pub base: BaseNode,
308 pub expression: Box<Expression>,
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize)]
312pub struct IfStatement {
313 #[serde(flatten)]
314 pub base: BaseNode,
315 pub test: Box<Expression>,
316 pub consequent: Box<Statement>,
317 pub alternate: Option<Box<Statement>>,
318}
319
320#[derive(Debug, Clone, Serialize, Deserialize)]
321pub struct ForStatement {
322 #[serde(flatten)]
323 pub base: BaseNode,
324 pub init: Option<Box<ForInit>>,
325 pub test: Option<Box<Expression>>,
326 pub update: Option<Box<Expression>>,
327 pub body: Box<Statement>,
328}
329
330#[derive(Debug, Clone, Serialize, Deserialize)]
331#[serde(tag = "type")]
332pub enum ForInit {
333 VariableDeclaration(VariableDeclaration),
334 #[serde(untagged)]
335 Expression(Box<Expression>),
336}
337
338#[derive(Debug, Clone, Serialize, Deserialize)]
339pub struct WhileStatement {
340 #[serde(flatten)]
341 pub base: BaseNode,
342 pub test: Box<Expression>,
343 pub body: Box<Statement>,
344}
345
346#[derive(Debug, Clone, Serialize, Deserialize)]
347pub struct DoWhileStatement {
348 #[serde(flatten)]
349 pub base: BaseNode,
350 pub test: Box<Expression>,
351 pub body: Box<Statement>,
352}
353
354#[derive(Debug, Clone, Serialize, Deserialize)]
355pub struct ForInStatement {
356 #[serde(flatten)]
357 pub base: BaseNode,
358 pub left: Box<ForInOfLeft>,
359 pub right: Box<Expression>,
360 pub body: Box<Statement>,
361}
362
363#[derive(Debug, Clone, Serialize, Deserialize)]
364pub struct ForOfStatement {
365 #[serde(flatten)]
366 pub base: BaseNode,
367 pub left: Box<ForInOfLeft>,
368 pub right: Box<Expression>,
369 pub body: Box<Statement>,
370 #[serde(default, rename = "await")]
371 pub is_await: bool,
372}
373
374#[derive(Debug, Clone, Serialize, Deserialize)]
375#[serde(tag = "type")]
376pub enum ForInOfLeft {
377 VariableDeclaration(VariableDeclaration),
378 #[serde(untagged)]
379 Pattern(Box<PatternLike>),
380}
381
382#[derive(Debug, Clone, Serialize, Deserialize)]
383pub struct SwitchStatement {
384 #[serde(flatten)]
385 pub base: BaseNode,
386 pub discriminant: Box<Expression>,
387 pub cases: Vec<SwitchCase>,
388}
389
390#[derive(Debug, Clone, Serialize, Deserialize)]
391pub struct SwitchCase {
392 #[serde(flatten)]
393 pub base: BaseNode,
394 pub test: Option<Box<Expression>>,
395 pub consequent: Vec<Statement>,
396}
397
398#[derive(Debug, Clone, Serialize, Deserialize)]
399pub struct ThrowStatement {
400 #[serde(flatten)]
401 pub base: BaseNode,
402 pub argument: Box<Expression>,
403}
404
405#[derive(Debug, Clone, Serialize, Deserialize)]
406pub struct TryStatement {
407 #[serde(flatten)]
408 pub base: BaseNode,
409 pub block: BlockStatement,
410 pub handler: Option<CatchClause>,
411 pub finalizer: Option<BlockStatement>,
412}
413
414#[derive(Debug, Clone, Serialize, Deserialize)]
415pub struct CatchClause {
416 #[serde(flatten)]
417 pub base: BaseNode,
418 pub param: Option<PatternLike>,
419 pub body: BlockStatement,
420}
421
422#[derive(Debug, Clone, Serialize, Deserialize)]
423pub struct BreakStatement {
424 #[serde(flatten)]
425 pub base: BaseNode,
426 pub label: Option<Identifier>,
427}
428
429#[derive(Debug, Clone, Serialize, Deserialize)]
430pub struct ContinueStatement {
431 #[serde(flatten)]
432 pub base: BaseNode,
433 pub label: Option<Identifier>,
434}
435
436#[derive(Debug, Clone, Serialize, Deserialize)]
437pub struct LabeledStatement {
438 #[serde(flatten)]
439 pub base: BaseNode,
440 pub label: Identifier,
441 pub body: Box<Statement>,
442}
443
444#[derive(Debug, Clone, Serialize, Deserialize)]
445pub struct EmptyStatement {
446 #[serde(flatten)]
447 pub base: BaseNode,
448}
449
450#[derive(Debug, Clone, Serialize, Deserialize)]
451pub struct DebuggerStatement {
452 #[serde(flatten)]
453 pub base: BaseNode,
454}
455
456#[derive(Debug, Clone, Serialize, Deserialize)]
457pub struct WithStatement {
458 #[serde(flatten)]
459 pub base: BaseNode,
460 pub object: Box<Expression>,
461 pub body: Box<Statement>,
462}
463
464#[derive(Debug, Clone, Serialize, Deserialize)]
465pub struct VariableDeclaration {
466 #[serde(flatten)]
467 pub base: BaseNode,
468 pub declarations: Vec<VariableDeclarator>,
469 pub kind: VariableDeclarationKind,
470 #[serde(default, skip_serializing_if = "Option::is_none")]
471 pub declare: Option<bool>,
472}
473
474#[derive(Debug, Clone, Serialize, Deserialize)]
475#[serde(rename_all = "lowercase")]
476pub enum VariableDeclarationKind {
477 Var,
478 Let,
479 Const,
480 Using,
481}
482
483#[derive(Debug, Clone, Serialize, Deserialize)]
484pub struct VariableDeclarator {
485 #[serde(flatten)]
486 pub base: BaseNode,
487 pub id: PatternLike,
488 pub init: Option<Box<Expression>>,
489 #[serde(default, skip_serializing_if = "Option::is_none")]
490 pub definite: Option<bool>,
491}
492
493#[derive(Debug, Clone, Serialize, Deserialize)]
494pub struct FunctionDeclaration {
495 #[serde(flatten)]
496 pub base: BaseNode,
497 pub id: Option<Identifier>,
498 pub params: Vec<PatternLike>,
499 pub body: BlockStatement,
500 #[serde(default)]
501 pub generator: bool,
502 #[serde(default, rename = "async")]
503 pub is_async: bool,
504 #[serde(default, skip_serializing_if = "Option::is_none")]
505 pub declare: Option<bool>,
506 #[serde(
507 default,
508 skip_serializing_if = "Option::is_none",
509 rename = "returnType"
510 )]
511 pub return_type: Option<Box<serde_json::Value>>,
512 #[serde(
513 default,
514 skip_serializing_if = "Option::is_none",
515 rename = "typeParameters"
516 )]
517 pub type_parameters: Option<Box<serde_json::Value>>,
518 #[serde(
519 default,
520 skip_serializing_if = "Option::is_none",
521 rename = "predicate",
522 deserialize_with = "crate::common::nullable_value"
523 )]
524 pub predicate: Option<Box<serde_json::Value>>,
525 #[serde(
527 default,
528 skip_serializing_if = "is_false",
529 rename = "__componentDeclaration"
530 )]
531 pub component_declaration: bool,
532 #[serde(
534 default,
535 skip_serializing_if = "is_false",
536 rename = "__hookDeclaration"
537 )]
538 pub hook_declaration: bool,
539}
540
541#[derive(Debug, Clone, Serialize, Deserialize)]
542pub struct ClassDeclaration {
543 #[serde(flatten)]
544 pub base: BaseNode,
545 pub id: Option<Identifier>,
546 #[serde(rename = "superClass")]
547 pub super_class: Option<Box<Expression>>,
548 pub body: crate::expressions::ClassBody,
549 #[serde(default, skip_serializing_if = "Option::is_none")]
550 pub decorators: Option<Vec<serde_json::Value>>,
551 #[serde(default, skip_serializing_if = "Option::is_none", rename = "abstract")]
552 pub is_abstract: Option<bool>,
553 #[serde(default, skip_serializing_if = "Option::is_none")]
554 pub declare: Option<bool>,
555 #[serde(
556 default,
557 skip_serializing_if = "Option::is_none",
558 rename = "implements"
559 )]
560 pub implements: Option<Vec<serde_json::Value>>,
561 #[serde(
562 default,
563 skip_serializing_if = "Option::is_none",
564 rename = "superTypeParameters"
565 )]
566 pub super_type_parameters: Option<Box<serde_json::Value>>,
567 #[serde(
568 default,
569 skip_serializing_if = "Option::is_none",
570 rename = "typeParameters"
571 )]
572 pub type_parameters: Option<Box<serde_json::Value>>,
573 #[serde(default, skip_serializing_if = "Option::is_none")]
574 pub mixins: Option<Vec<serde_json::Value>>,
575}
576
577#[cfg(test)]
578mod tests {
579 use serde_json::json;
580
581 use super::Statement;
582
583 #[test]
584 fn unknown_statement_round_trips_at_program_level() {
585 let input = json!({
586 "type": "File",
587 "comments": [],
588 "errors": [],
589 "program": {
590 "type": "Program",
591 "sourceType": "module",
592 "interpreter": null,
593 "body": [
594 {
595 "type": "TSImportEqualsDeclaration",
596 "start": 0,
597 "end": 39,
598 "importKind": "value",
599 "isExport": false,
600 "id": { "type": "Identifier", "name": "lib" },
601 "moduleReference": {
602 "type": "TSExternalModuleReference",
603 "expression": { "type": "StringLiteral", "value": "shared-runtime" }
604 }
605 }
606 ],
607 "directives": []
608 }
609 });
610
611 let file: crate::File = serde_json::from_value(input.clone()).unwrap();
612
613 match &file.program.body[0] {
614 Statement::Unknown(unknown) => {
615 assert_eq!(unknown.node_type(), "TSImportEqualsDeclaration");
616 }
617 other => panic!("expected Unknown, got {other:?}"),
618 }
619 assert_eq!(serde_json::to_value(&file).unwrap(), input);
620 }
621
622 #[test]
623 fn unknown_statement_round_trips_inside_function_block() {
624 let input = json!({
625 "type": "FunctionDeclaration",
626 "id": null,
627 "generator": false,
628 "async": false,
629 "params": [],
630 "body": {
631 "type": "BlockStatement",
632 "body": [
633 {
634 "type": "TSExportAssignment",
635 "expression": { "type": "Identifier", "name": "x" }
636 }
637 ],
638 "directives": []
639 }
640 });
641
642 let stmt: Statement = serde_json::from_value(input.clone()).unwrap();
643 let Statement::FunctionDeclaration(function) = &stmt else {
644 panic!("expected function declaration, got {stmt:?}");
645 };
646 assert!(matches!(function.body.body[0], Statement::Unknown(_)));
647 assert_eq!(serde_json::to_value(&stmt).unwrap(), input);
648 }
649
650 #[test]
653 fn is_known_statement_type_matches_macro_list() {
654 assert!(super::is_known_statement_type("IfStatement"));
655 assert!(super::is_known_statement_type("VariableDeclaration"));
656 assert!(!super::is_known_statement_type("CallExpression"));
657 assert!(!super::is_known_statement_type("TSImportEqualsDeclaration"));
658 }
659
660 #[test]
661 fn known_statement_type_uses_typed_variant() {
662 let stmt: Statement = serde_json::from_value(json!({
663 "type": "EmptyStatement"
664 }))
665 .unwrap();
666
667 assert!(matches!(stmt, Statement::EmptyStatement(_)));
668 }
669
670 #[test]
671 fn malformed_known_statement_type_errors() {
672 let err = serde_json::from_value::<Statement>(json!({
673 "type": "IfStatement",
674 "consequent": {
675 "type": "EmptyStatement"
676 }
677 }))
678 .unwrap_err();
679
680 assert!(
681 err.to_string().contains("missing field `test`"),
682 "unexpected error: {err}"
683 );
684 }
685
686 #[test]
687 fn statement_without_type_field_errors() {
688 let err = serde_json::from_value::<Statement>(json!({
689 "start": 0,
690 "end": 1
691 }))
692 .unwrap_err();
693
694 assert!(
695 err.to_string().contains("`type`"),
696 "unexpected error: {err}"
697 );
698 }
699
700 #[test]
701 fn non_object_statement_errors() {
702 let err = serde_json::from_value::<Statement>(json!([1, 2])).unwrap_err();
703 assert!(
704 err.to_string().contains("`type`"),
705 "unexpected error: {err}"
706 );
707 }
708
709 #[test]
710 fn non_string_type_field_errors() {
711 let err = serde_json::from_value::<Statement>(json!({ "type": 7 })).unwrap_err();
712 assert!(
713 err.to_string().contains("`type`"),
714 "unexpected error: {err}"
715 );
716 }
717
718 #[test]
721 fn with_raw_mut_refreshes_base_and_guards_type() {
722 let raw = json!({
723 "type": "TSExportAssignment",
724 "start": 5,
725 "expression": { "type": "Identifier", "name": "x" }
726 });
727 let Statement::Unknown(mut unknown) = serde_json::from_value(raw).unwrap() else {
728 panic!("expected Unknown");
729 };
730
731 unknown
732 .with_raw_mut(|v| {
733 v["start"] = json!(9);
734 v["expression"]["name"] = json!("y");
735 })
736 .unwrap();
737 assert_eq!(unknown.base().start, Some(9));
738 assert_eq!(unknown.raw()["expression"]["name"], json!("y"));
739
740 let err = unknown.with_raw_mut(|v| {
741 v.as_object_mut().unwrap().remove("type");
742 });
743 assert!(err.is_err(), "type removal must be rejected");
744 }
745}