rfmt 0.1.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,335 @@
1
+ use serde::{Deserialize, Serialize};
2
+ use std::collections::HashMap;
3
+
4
+ /// Internal AST representation
5
+ /// This structure is designed to work seamlessly with Prism parser output
6
+ #[derive(Debug, Clone, Serialize, Deserialize)]
7
+ pub struct Node {
8
+ pub node_type: NodeType,
9
+ pub location: Location,
10
+ pub children: Vec<Node>,
11
+ pub metadata: HashMap<String, String>,
12
+ pub comments: Vec<Comment>,
13
+ pub formatting: FormattingInfo,
14
+ }
15
+
16
+ /// Location information for a node in the source code
17
+ #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
18
+ pub struct Location {
19
+ pub start_line: usize,
20
+ pub start_column: usize,
21
+ pub end_line: usize,
22
+ pub end_column: usize,
23
+ pub start_offset: usize,
24
+ pub end_offset: usize,
25
+ }
26
+
27
+ /// Node types supported by rfmt
28
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
29
+ #[serde(rename_all = "snake_case")]
30
+ pub enum NodeType {
31
+ // Program & Statements
32
+ ProgramNode,
33
+ StatementsNode,
34
+
35
+ // Definitions
36
+ ClassNode,
37
+ ModuleNode,
38
+ DefNode,
39
+
40
+ // Expressions
41
+ CallNode,
42
+ IfNode,
43
+ UnlessNode,
44
+
45
+ // Literals
46
+ StringNode,
47
+ IntegerNode,
48
+ FloatNode,
49
+ ArrayNode,
50
+ HashNode,
51
+ TrueNode,
52
+ FalseNode,
53
+ NilNode,
54
+
55
+ // Blocks
56
+ BlockNode,
57
+
58
+ // Constants (structural nodes, part of definitions)
59
+ ConstantReadNode,
60
+ ConstantWriteNode,
61
+ ConstantPathNode,
62
+
63
+ // Parameters (structural nodes, part of method definitions)
64
+ RequiredParameterNode,
65
+ OptionalParameterNode,
66
+ RestParameterNode,
67
+ KeywordParameterNode,
68
+ KeywordRestParameterNode,
69
+ BlockParameterNode,
70
+
71
+ Unknown(String),
72
+ }
73
+
74
+ impl NodeType {
75
+ /// Parse node type from Prism type string
76
+ /// Returns Unknown variant for unsupported node types
77
+ pub fn from_str(s: &str) -> Self {
78
+ match s {
79
+ "program_node" => Self::ProgramNode,
80
+ "statements_node" => Self::StatementsNode,
81
+ "class_node" => Self::ClassNode,
82
+ "module_node" => Self::ModuleNode,
83
+ "def_node" => Self::DefNode,
84
+ "call_node" => Self::CallNode,
85
+ "if_node" => Self::IfNode,
86
+ "unless_node" => Self::UnlessNode,
87
+ "string_node" => Self::StringNode,
88
+ "integer_node" => Self::IntegerNode,
89
+ "float_node" => Self::FloatNode,
90
+ "array_node" => Self::ArrayNode,
91
+ "hash_node" => Self::HashNode,
92
+ "true_node" => Self::TrueNode,
93
+ "false_node" => Self::FalseNode,
94
+ "nil_node" => Self::NilNode,
95
+ "block_node" => Self::BlockNode,
96
+ "constant_read_node" => Self::ConstantReadNode,
97
+ "constant_write_node" => Self::ConstantWriteNode,
98
+ "constant_path_node" => Self::ConstantPathNode,
99
+ "required_parameter_node" => Self::RequiredParameterNode,
100
+ "optional_parameter_node" => Self::OptionalParameterNode,
101
+ "rest_parameter_node" => Self::RestParameterNode,
102
+ "keyword_parameter_node" => Self::KeywordParameterNode,
103
+ "keyword_rest_parameter_node" => Self::KeywordRestParameterNode,
104
+ "block_parameter_node" => Self::BlockParameterNode,
105
+ _ => Self::Unknown(s.to_string()),
106
+ }
107
+ }
108
+
109
+ /// Check if this node type is a definition (class, module, or method)
110
+ #[cfg(test)]
111
+ pub fn is_definition(&self) -> bool {
112
+ matches!(
113
+ self,
114
+ NodeType::ClassNode | NodeType::ModuleNode | NodeType::DefNode
115
+ )
116
+ }
117
+ }
118
+
119
+ /// Comment attached to a node
120
+ #[derive(Debug, Clone, Serialize, Deserialize)]
121
+ pub struct Comment {
122
+ pub text: String,
123
+ pub location: Location,
124
+ pub comment_type: CommentType,
125
+ pub position: CommentPosition,
126
+ }
127
+
128
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
129
+ pub enum CommentType {
130
+ Line, // # comment
131
+ Block, // =begin...=end
132
+ }
133
+
134
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
135
+ pub enum CommentPosition {
136
+ Leading, // Comment before the node
137
+ Trailing, // Comment after the node (same line)
138
+ Inner, // Comment inside the node
139
+ }
140
+
141
+ /// Formatting information attached to a node
142
+ #[derive(Debug, Clone, Default, Serialize, Deserialize)]
143
+ pub struct FormattingInfo {
144
+ pub indent_level: usize,
145
+ pub needs_blank_line_before: bool,
146
+ pub needs_blank_line_after: bool,
147
+ pub preserve_newlines: bool,
148
+ pub multiline: bool,
149
+ pub original_formatting: Option<String>,
150
+ }
151
+
152
+ impl Node {
153
+ /// Create a new node with the given type and location
154
+ #[cfg(test)]
155
+ pub fn new(node_type: NodeType, location: Location) -> Self {
156
+ Self {
157
+ node_type,
158
+ location,
159
+ children: Vec::new(),
160
+ metadata: HashMap::new(),
161
+ comments: Vec::new(),
162
+ formatting: FormattingInfo::default(),
163
+ }
164
+ }
165
+
166
+ /// Add children to the node
167
+ #[cfg(test)]
168
+ pub fn with_children(mut self, children: Vec<Node>) -> Self {
169
+ self.children = children;
170
+ self
171
+ }
172
+
173
+ /// Add metadata to the node
174
+ #[cfg(test)]
175
+ pub fn with_metadata(mut self, metadata: HashMap<String, String>) -> Self {
176
+ self.metadata = metadata;
177
+ self
178
+ }
179
+
180
+ /// Add comments to the node
181
+ #[cfg(test)]
182
+ pub fn with_comments(mut self, comments: Vec<Comment>) -> Self {
183
+ self.comments = comments;
184
+ self
185
+ }
186
+
187
+ /// Check if the node spans multiple lines
188
+ #[cfg(test)]
189
+ pub fn is_multiline(&self) -> bool {
190
+ self.location.start_line != self.location.end_line
191
+ }
192
+
193
+ /// Get the number of lines this node spans
194
+ #[cfg(test)]
195
+ pub fn line_count(&self) -> usize {
196
+ self.location.end_line - self.location.start_line + 1
197
+ }
198
+
199
+ /// Check if this is an unknown node type
200
+ #[cfg(test)]
201
+ pub fn is_unknown(&self) -> bool {
202
+ matches!(self.node_type, NodeType::Unknown(_))
203
+ }
204
+
205
+ /// Get the unknown node type name if this is an unknown node
206
+ #[cfg(test)]
207
+ pub fn unknown_type(&self) -> Option<&str> {
208
+ match &self.node_type {
209
+ NodeType::Unknown(name) => Some(name.as_str()),
210
+ _ => None,
211
+ }
212
+ }
213
+ }
214
+
215
+ impl Location {
216
+ /// Create a new location
217
+ pub fn new(
218
+ start_line: usize,
219
+ start_column: usize,
220
+ end_line: usize,
221
+ end_column: usize,
222
+ start_offset: usize,
223
+ end_offset: usize,
224
+ ) -> Self {
225
+ Self {
226
+ start_line,
227
+ start_column,
228
+ end_line,
229
+ end_column,
230
+ start_offset,
231
+ end_offset,
232
+ }
233
+ }
234
+
235
+ /// Create a zero location (for testing)
236
+ #[cfg(test)]
237
+ pub fn zero() -> Self {
238
+ Self {
239
+ start_line: 0,
240
+ start_column: 0,
241
+ end_line: 0,
242
+ end_column: 0,
243
+ start_offset: 0,
244
+ end_offset: 0,
245
+ }
246
+ }
247
+ }
248
+
249
+ #[cfg(test)]
250
+ mod tests {
251
+ use super::*;
252
+
253
+ #[test]
254
+ fn test_node_creation() {
255
+ let node = Node::new(NodeType::ProgramNode, Location::zero());
256
+ assert_eq!(node.node_type, NodeType::ProgramNode);
257
+ assert_eq!(node.children.len(), 0);
258
+ }
259
+
260
+ #[test]
261
+ fn test_node_with_children() {
262
+ let child = Node::new(NodeType::ClassNode, Location::zero());
263
+ let node = Node::new(NodeType::ProgramNode, Location::zero()).with_children(vec![child]);
264
+
265
+ assert_eq!(node.children.len(), 1);
266
+ assert_eq!(node.children[0].node_type, NodeType::ClassNode);
267
+ }
268
+
269
+ #[test]
270
+ fn test_node_type_from_str() {
271
+ assert_eq!(NodeType::from_str("program_node"), NodeType::ProgramNode);
272
+ assert_eq!(NodeType::from_str("class_node"), NodeType::ClassNode);
273
+ assert_eq!(NodeType::from_str("def_node"), NodeType::DefNode);
274
+
275
+ // Unknown type should return Unknown variant
276
+ match NodeType::from_str("unknown_node") {
277
+ NodeType::Unknown(s) => assert_eq!(s, "unknown_node"),
278
+ _ => panic!("Expected Unknown variant"),
279
+ }
280
+ }
281
+
282
+ #[test]
283
+ fn test_node_type_is_definition() {
284
+ assert!(NodeType::ClassNode.is_definition());
285
+ assert!(NodeType::ModuleNode.is_definition());
286
+ assert!(NodeType::DefNode.is_definition());
287
+ assert!(!NodeType::CallNode.is_definition());
288
+ assert!(!NodeType::IntegerNode.is_definition());
289
+ }
290
+
291
+ #[test]
292
+ fn test_location_zero() {
293
+ let loc = Location::zero();
294
+ assert_eq!(loc.start_line, 0);
295
+ assert_eq!(loc.start_offset, 0);
296
+ }
297
+
298
+ #[test]
299
+ fn test_node_is_multiline() {
300
+ let single_line = Node::new(NodeType::CallNode, Location::new(1, 0, 1, 10, 0, 10));
301
+ assert!(!single_line.is_multiline());
302
+
303
+ let multi_line = Node::new(NodeType::ClassNode, Location::new(1, 0, 5, 3, 0, 50));
304
+ assert!(multi_line.is_multiline());
305
+ }
306
+
307
+ #[test]
308
+ fn test_node_line_count() {
309
+ let node = Node::new(NodeType::DefNode, Location::new(10, 0, 15, 3, 100, 200));
310
+ assert_eq!(node.line_count(), 6);
311
+ }
312
+
313
+ #[test]
314
+ fn test_node_is_unknown() {
315
+ let known_node = Node::new(NodeType::ClassNode, Location::zero());
316
+ assert!(!known_node.is_unknown());
317
+ assert_eq!(known_node.unknown_type(), None);
318
+
319
+ let unknown_node = Node::new(
320
+ NodeType::Unknown("custom_node".to_string()),
321
+ Location::zero(),
322
+ );
323
+ assert!(unknown_node.is_unknown());
324
+ assert_eq!(unknown_node.unknown_type(), Some("custom_node"));
325
+ }
326
+
327
+ #[test]
328
+ fn test_node_type_from_str_returns_unknown_for_unsupported() {
329
+ let node_type = NodeType::from_str("unsupported_node_type");
330
+ match node_type {
331
+ NodeType::Unknown(name) => assert_eq!(name, "unsupported_node_type"),
332
+ _ => panic!("Expected Unknown variant"),
333
+ }
334
+ }
335
+ }