main
rs 193 lines 7.03 KB
Raw
1 use serde::Deserialize;
2 use serde::Serialize;
3
4 /// An AST subtree the compiler does not model with typed nodes (type
5 /// annotations, class bodies, parser extras). Wraps JSON text: serialization
6 /// is verbatim pass-through and deserialization streams the subtree into text
7 /// without retaining a `serde_json::Value` tree. Consumers that inspect these
8 /// subtrees parse on demand via [`RawNode::parse_value`]; paths that do so
9 /// repeatedly per traversal pay a parse each time, so cache the parsed Value
10 /// at the call site if it shows up in profiles.
11 ///
12 /// Deserialize is hand-implemented with a transcode rather than capturing a
13 /// `RawValue` directly: most nodes sit under `#[serde(tag = "type")]` enums,
14 /// whose content buffering breaks `RawValue`'s text-borrowing capture.
15 #[derive(Debug, Clone, Serialize)]
16 #[serde(transparent)]
17 pub struct RawNode(pub Box<serde_json::value::RawValue>);
18
19 impl<'de> serde::Deserialize<'de> for RawNode {
20 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
21 where
22 D: serde::Deserializer<'de>,
23 {
24 let mut buf = Vec::new();
25 let mut ser = serde_json::Serializer::new(&mut buf);
26 serde_transcode::transcode(deserializer, &mut ser).map_err(serde::de::Error::custom)?;
27 let text = String::from_utf8(buf).map_err(serde::de::Error::custom)?;
28 serde_json::value::RawValue::from_string(text)
29 .map(RawNode)
30 .map_err(serde::de::Error::custom)
31 }
32 }
33
34 impl RawNode {
35 pub fn from_value(value: &serde_json::Value) -> Self {
36 RawNode(
37 serde_json::value::RawValue::from_string(value.to_string())
38 .expect("serde_json::Value always serializes to valid JSON"),
39 )
40 }
41
42 pub fn null() -> Self {
43 RawNode(
44 serde_json::value::RawValue::from_string("null".to_string())
45 .expect("null is valid JSON"),
46 )
47 }
48
49 /// The raw JSON text of this subtree.
50 pub fn get(&self) -> &str {
51 self.0.get()
52 }
53
54 /// Parse the subtree into a `serde_json::Value` for structural inspection.
55 /// RawNode text is valid JSON by construction, so failure here means a
56 /// broken invariant, not bad input; fail loudly rather than degrade.
57 pub fn parse_value(&self) -> serde_json::Value {
58 from_json_str_unbounded(self.0.get()).expect("RawNode holds valid JSON by construction")
59 }
60
61 /// The node's `"type"` field, without parsing the whole subtree into a Value.
62 pub fn type_name(&self) -> Option<String> {
63 #[derive(Deserialize)]
64 struct TypeProbe {
65 #[serde(rename = "type")]
66 type_name: Option<String>,
67 }
68 from_json_str_unbounded::<TypeProbe>(self.0.get())
69 .ok()
70 .and_then(|p| p.type_name)
71 }
72 }
73
74 /// Parse JSON text with serde_json's recursion limit disabled. Every internal
75 /// reparse of [`RawNode`] text must go through this: the napi entrypoint
76 /// deserializes arbitrarily deep ASTs with the limit disabled (on a 64MB
77 /// stack), and the tolerant statement path's reparses must not quietly
78 /// reintroduce the default limit.
79 pub fn from_json_str_unbounded<'de, T: serde::Deserialize<'de>>(
80 s: &'de str,
81 ) -> serde_json::Result<T> {
82 let mut deserializer = serde_json::Deserializer::from_str(s);
83 deserializer.disable_recursion_limit();
84 T::deserialize(&mut deserializer)
85 }
86
87 /// Custom deserializer that distinguishes "field absent" from "field: null".
88 /// - JSON field absent → `None` (via `#[serde(default)]`)
89 /// - JSON field `null` → `Some(RawNode("null"))`
90 /// - JSON field with value → `Some(raw value)`
91 ///
92 /// Use with `#[serde(default, skip_serializing_if = "Option::is_none", deserialize_with = "nullable_value")]`
93 pub fn nullable_value<'de, D>(deserializer: D) -> Result<Option<RawNode>, D::Error>
94 where
95 D: serde::Deserializer<'de>,
96 {
97 RawNode::deserialize(deserializer).map(Some)
98 }
99
100 #[derive(Debug, Clone, Serialize, Deserialize)]
101 pub struct Position {
102 pub line: u32,
103 pub column: u32,
104 #[serde(default, skip_serializing_if = "Option::is_none")]
105 pub index: Option<u32>,
106 }
107
108 #[derive(Debug, Clone, Serialize, Deserialize)]
109 pub struct SourceLocation {
110 pub start: Position,
111 pub end: Position,
112 #[serde(default, skip_serializing_if = "Option::is_none")]
113 pub filename: Option<String>,
114 #[serde(
115 default,
116 skip_serializing_if = "Option::is_none",
117 rename = "identifierName"
118 )]
119 pub identifier_name: Option<String>,
120 }
121
122 #[derive(Debug, Clone, Serialize, Deserialize)]
123 #[serde(tag = "type")]
124 pub enum Comment {
125 CommentBlock(CommentData),
126 CommentLine(CommentData),
127 }
128
129 #[derive(Debug, Clone, Serialize, Deserialize)]
130 pub struct CommentData {
131 pub value: String,
132 #[serde(default, skip_serializing_if = "Option::is_none")]
133 pub start: Option<u32>,
134 #[serde(default, skip_serializing_if = "Option::is_none")]
135 pub end: Option<u32>,
136 #[serde(default, skip_serializing_if = "Option::is_none")]
137 pub loc: Option<SourceLocation>,
138 }
139
140 #[derive(Debug, Clone, Default, Serialize, Deserialize)]
141 pub struct BaseNode {
142 // NOTE: When creating AST nodes for code generation output, use
143 // `BaseNode::typed("NodeTypeName")` instead of `BaseNode::default()`
144 // to ensure the "type" field is emitted during serialization.
145 /// The node type string (e.g. "BlockStatement").
146 /// When deserialized through a `#[serde(tag = "type")]` enum, the enum
147 /// consumes the "type" field so this defaults to None. When deserialized
148 /// directly, this captures the "type" field for round-trip fidelity.
149 #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
150 pub node_type: Option<String>,
151 #[serde(default, skip_serializing_if = "Option::is_none")]
152 pub start: Option<u32>,
153 #[serde(default, skip_serializing_if = "Option::is_none")]
154 pub end: Option<u32>,
155 #[serde(default, skip_serializing_if = "Option::is_none")]
156 pub loc: Option<SourceLocation>,
157 #[serde(default, skip_serializing_if = "Option::is_none")]
158 pub range: Option<(u32, u32)>,
159 #[serde(default, skip_serializing_if = "Option::is_none")]
160 pub extra: Option<RawNode>,
161 #[serde(
162 default,
163 skip_serializing_if = "Option::is_none",
164 rename = "leadingComments"
165 )]
166 pub leading_comments: Option<Vec<Comment>>,
167 #[serde(
168 default,
169 skip_serializing_if = "Option::is_none",
170 rename = "innerComments"
171 )]
172 pub inner_comments: Option<Vec<Comment>>,
173 #[serde(
174 default,
175 skip_serializing_if = "Option::is_none",
176 rename = "trailingComments"
177 )]
178 pub trailing_comments: Option<Vec<Comment>>,
179 #[serde(default, skip_serializing_if = "Option::is_none", rename = "_nodeId")]
180 pub node_id: Option<u32>,
181 }
182
183 impl BaseNode {
184 /// Create a BaseNode with the given type name.
185 /// Use this when creating AST nodes for code generation to ensure the
186 /// `"type"` field is present in serialized output.
187 pub fn typed(type_name: &str) -> Self {
188 Self {
189 node_type: Some(type_name.to_string()),
190 ..Default::default()
191 }
192 }
193 }