Skip to main content

react_compiler_ast/
common.rs

1use serde::Deserialize;
2use serde::Serialize;
3
4/// Custom deserializer that distinguishes "field absent" from "field: null".
5/// - JSON field absent → `None` (via `#[serde(default)]`)
6/// - JSON field `null` → `Some(Value::Null)`
7/// - JSON field with value → `Some(value)`
8///
9/// Use with `#[serde(default, skip_serializing_if = "Option::is_none", deserialize_with = "nullable_value")]`
10pub fn nullable_value<'de, D>(deserializer: D) -> Result<Option<Box<serde_json::Value>>, D::Error>
11where
12    D: serde::Deserializer<'de>,
13{
14    let value = serde_json::Value::deserialize(deserializer)?;
15    Ok(Some(Box::new(value)))
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct Position {
20    pub line: u32,
21    pub column: u32,
22    #[serde(default, skip_serializing_if = "Option::is_none")]
23    pub index: Option<u32>,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct SourceLocation {
28    pub start: Position,
29    pub end: Position,
30    #[serde(default, skip_serializing_if = "Option::is_none")]
31    pub filename: Option<String>,
32    #[serde(
33        default,
34        skip_serializing_if = "Option::is_none",
35        rename = "identifierName"
36    )]
37    pub identifier_name: Option<String>,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
41#[serde(tag = "type")]
42pub enum Comment {
43    CommentBlock(CommentData),
44    CommentLine(CommentData),
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct CommentData {
49    pub value: String,
50    #[serde(default, skip_serializing_if = "Option::is_none")]
51    pub start: Option<u32>,
52    #[serde(default, skip_serializing_if = "Option::is_none")]
53    pub end: Option<u32>,
54    #[serde(default, skip_serializing_if = "Option::is_none")]
55    pub loc: Option<SourceLocation>,
56}
57
58#[derive(Debug, Clone, Default, Serialize, Deserialize)]
59pub struct BaseNode {
60    // NOTE: When creating AST nodes for code generation output, use
61    // `BaseNode::typed("NodeTypeName")` instead of `BaseNode::default()`
62    // to ensure the "type" field is emitted during serialization.
63    /// The node type string (e.g. "BlockStatement").
64    /// When deserialized through a `#[serde(tag = "type")]` enum, the enum
65    /// consumes the "type" field so this defaults to None. When deserialized
66    /// directly, this captures the "type" field for round-trip fidelity.
67    #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
68    pub node_type: Option<String>,
69    #[serde(default, skip_serializing_if = "Option::is_none")]
70    pub start: Option<u32>,
71    #[serde(default, skip_serializing_if = "Option::is_none")]
72    pub end: Option<u32>,
73    #[serde(default, skip_serializing_if = "Option::is_none")]
74    pub loc: Option<SourceLocation>,
75    #[serde(default, skip_serializing_if = "Option::is_none")]
76    pub range: Option<(u32, u32)>,
77    #[serde(default, skip_serializing_if = "Option::is_none")]
78    pub extra: Option<serde_json::Value>,
79    #[serde(
80        default,
81        skip_serializing_if = "Option::is_none",
82        rename = "leadingComments"
83    )]
84    pub leading_comments: Option<Vec<Comment>>,
85    #[serde(
86        default,
87        skip_serializing_if = "Option::is_none",
88        rename = "innerComments"
89    )]
90    pub inner_comments: Option<Vec<Comment>>,
91    #[serde(
92        default,
93        skip_serializing_if = "Option::is_none",
94        rename = "trailingComments"
95    )]
96    pub trailing_comments: Option<Vec<Comment>>,
97    #[serde(default, skip_serializing_if = "Option::is_none", rename = "_nodeId")]
98    pub node_id: Option<u32>,
99}
100
101impl BaseNode {
102    /// Create a BaseNode with the given type name.
103    /// Use this when creating AST nodes for code generation to ensure the
104    /// `"type"` field is present in serialized output.
105    pub fn typed(type_name: &str) -> Self {
106        Self {
107            node_type: Some(type_name.to_string()),
108            ..Default::default()
109        }
110    }
111}