rbs 4.0.1.dev.1 → 4.0.1.dev.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.
@@ -1,764 +0,0 @@
1
- use serde::Deserialize;
2
- use std::{env, error::Error, fs::File, io::Write, path::Path};
3
-
4
- // This config-driven code generation approach is inspired by Prism's ruby-prism crate.
5
- // See: https://github.com/ruby/prism/blob/main/rust/ruby-prism/build.rs
6
-
7
- #[derive(Debug, Deserialize)]
8
- struct Config {
9
- nodes: Vec<Node>,
10
- #[serde(default)]
11
- enums: std::collections::HashMap<String, EnumDef>,
12
- }
13
-
14
- #[derive(Debug, Deserialize)]
15
- struct EnumDef {
16
- #[serde(default)]
17
- #[allow(dead_code)]
18
- optional: bool,
19
- symbols: Vec<String>,
20
- }
21
-
22
- #[derive(Debug, Deserialize)]
23
- struct NodeField {
24
- name: String,
25
- c_type: String,
26
- c_name: Option<String>,
27
- #[serde(default)]
28
- optional: bool,
29
- }
30
-
31
- impl NodeField {
32
- fn c_name(&self) -> &str {
33
- let name = self.c_name.as_ref().unwrap_or(&self.name);
34
- if name == "type" { "type_" } else { name }
35
- }
36
- }
37
-
38
- #[derive(Debug, Deserialize)]
39
- struct LocationField {
40
- #[serde(default)]
41
- required: Option<String>,
42
- #[serde(default)]
43
- optional: Option<String>,
44
- }
45
-
46
- impl LocationField {
47
- fn name(&self) -> &str {
48
- self.required.as_ref().or(self.optional.as_ref()).unwrap()
49
- }
50
-
51
- fn is_required(&self) -> bool {
52
- self.required.is_some()
53
- }
54
- }
55
-
56
- #[derive(Debug, Deserialize)]
57
- struct Node {
58
- name: String,
59
- rust_name: String,
60
- fields: Option<Vec<NodeField>>,
61
- locations: Option<Vec<LocationField>>,
62
- }
63
-
64
- impl Node {
65
- fn variant_name(&self) -> &str {
66
- self.rust_name
67
- .strip_suffix("Node")
68
- .unwrap_or(&self.rust_name)
69
- }
70
- }
71
-
72
- fn main() -> Result<(), Box<dyn Error>> {
73
- let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
74
- let config_path = manifest_dir.join("vendor/rbs/config.yml");
75
-
76
- let config_path = config_path
77
- .canonicalize()
78
- .map_err(|e| format!("Failed to find config.yml at {:?}: {}", config_path, e))?;
79
-
80
- println!("cargo:rerun-if-changed={}", config_path.display());
81
-
82
- let config_file = File::open(&config_path)?;
83
- let mut config: Config = serde_yaml::from_reader(config_file)?;
84
-
85
- // Symbol represents identifiers (interned strings), not traditional AST nodes.
86
- // However, the C parser defines it in `rbs_node_type` (RBS_AST_SYMBOL) and
87
- // treats it as a node (rbs_node_t*) in many contexts (lists, hashes).
88
- // We inject it into the config so it is generated as a struct matching the Node pattern,
89
- // allowing it to be wrapped in the Node enum and handled uniformly in Rust.
90
- config.nodes.push(Node {
91
- name: "RBS::AST::Symbol".to_string(),
92
- rust_name: "SymbolNode".to_string(),
93
- fields: None,
94
- locations: None,
95
- });
96
-
97
- config.nodes.sort_by(|a, b| a.name.cmp(&b.name));
98
- generate(&config)?;
99
-
100
- Ok(())
101
- }
102
-
103
- enum CIdentifier {
104
- Type, // foo_bar_t
105
- Constant, // FOO_BAR
106
- Method, // visit_foo_bar
107
- }
108
-
109
- fn convert_name(name: &str, identifier: CIdentifier) -> String {
110
- let type_name = name.replace("::", "_");
111
- let lowercase = matches!(identifier, CIdentifier::Type | CIdentifier::Method);
112
- let mut out = String::new();
113
- let mut prev_is_lower = false;
114
-
115
- for ch in type_name.chars() {
116
- if ch.is_ascii_uppercase() {
117
- if prev_is_lower {
118
- out.push('_');
119
- }
120
- out.push(if lowercase {
121
- ch.to_ascii_lowercase()
122
- } else {
123
- ch
124
- });
125
- prev_is_lower = false;
126
- } else if ch == '_' {
127
- out.push(ch);
128
- prev_is_lower = false;
129
- } else {
130
- out.push(if lowercase {
131
- ch
132
- } else {
133
- ch.to_ascii_uppercase()
134
- });
135
- prev_is_lower = ch.is_ascii_lowercase() || ch.is_ascii_digit();
136
- }
137
- }
138
-
139
- if matches!(identifier, CIdentifier::Type) {
140
- out.push_str("_t");
141
- }
142
- out
143
- }
144
-
145
- /// Converts snake_case to PascalCase for Rust enum names
146
- fn snake_to_pascal(s: &str) -> String {
147
- s.split('_')
148
- .map(|word| {
149
- let mut chars = word.chars();
150
- match chars.next() {
151
- None => String::new(),
152
- Some(first) => first.to_uppercase().chain(chars).collect(),
153
- }
154
- })
155
- .collect()
156
- }
157
-
158
- /// Generates Rust enum module constant name from snake_case (e.g., "unspecified" -> "RBS_ATTRIBUTE_VISIBILITY_UNSPECIFIED")
159
- fn enum_constant_name(enum_name: &str, variant: &str) -> String {
160
- format!(
161
- "RBS_{}",
162
- format!("{}_{}", enum_name, variant).to_uppercase()
163
- )
164
- }
165
-
166
- fn write_enum_field_accessor(
167
- file: &mut File,
168
- field: &NodeField,
169
- enum_name: &str,
170
- ) -> std::io::Result<()> {
171
- let rust_enum_name = snake_to_pascal(enum_name);
172
- writeln!(file, " #[must_use]")?;
173
- writeln!(
174
- file,
175
- " pub fn {}(&self) -> {} {{",
176
- field.name, rust_enum_name
177
- )?;
178
- writeln!(
179
- file,
180
- " {}::from_raw(unsafe {{ (*self.pointer).{} }})",
181
- rust_enum_name,
182
- field.c_name()
183
- )?;
184
- writeln!(file, " }}")?;
185
- writeln!(file)?;
186
- Ok(())
187
- }
188
-
189
- fn write_node_field_accessor(
190
- file: &mut File,
191
- field: &NodeField,
192
- rust_type: &str,
193
- ) -> std::io::Result<()> {
194
- if field.optional {
195
- writeln!(file, " #[must_use]")?;
196
- writeln!(
197
- file,
198
- " pub fn {}(&self) -> Option<{rust_type}<'a>> {{",
199
- field.name,
200
- )?;
201
- writeln!(
202
- file,
203
- " let ptr = unsafe {{ (*self.pointer).{} }};",
204
- field.c_name()
205
- )?;
206
- writeln!(file, " if ptr.is_null() {{")?;
207
- writeln!(file, " None")?;
208
- writeln!(file, " }} else {{")?;
209
- writeln!(
210
- file,
211
- " Some({rust_type} {{ parser: self.parser, pointer: ptr, marker: PhantomData }})"
212
- )?;
213
- writeln!(file, " }}")?;
214
- } else {
215
- writeln!(file, " #[must_use]")?;
216
- writeln!(
217
- file,
218
- " pub fn {}(&self) -> {rust_type}<'a> {{",
219
- field.name
220
- )?;
221
- writeln!(
222
- file,
223
- " {rust_type} {{ parser: self.parser, pointer: unsafe {{ (*self.pointer).{} }}, marker: PhantomData }}",
224
- field.c_name()
225
- )?;
226
- }
227
- writeln!(file, " }}")?;
228
- writeln!(file)?;
229
- Ok(())
230
- }
231
-
232
- fn write_visit_trait(file: &mut File, config: &Config) -> Result<(), Box<dyn std::error::Error>> {
233
- writeln!(file, "/// A trait for traversing the AST using a visitor")?;
234
- writeln!(file, "pub trait Visit {{")?;
235
- writeln!(
236
- file,
237
- " /// Visit any node of the AST. Generally used to continue traversal"
238
- )?;
239
- writeln!(file, " fn visit(&mut self, node: &Node) {{")?;
240
- writeln!(file, " match node {{")?;
241
-
242
- for node in &config.nodes {
243
- let node_variant_name = node.variant_name();
244
- let method_name = convert_name(node_variant_name, CIdentifier::Method);
245
-
246
- writeln!(file, " Node::{node_variant_name}(it) => {{")?;
247
- writeln!(file, " self.visit_{method_name}_node(it);")?;
248
- writeln!(file, " }}")?;
249
- }
250
-
251
- writeln!(file, " }}")?;
252
- writeln!(file, " }}")?;
253
-
254
- for node in &config.nodes {
255
- let node_variant_name = node.variant_name();
256
- let method_name = convert_name(node_variant_name, CIdentifier::Method);
257
-
258
- writeln!(file)?;
259
- writeln!(
260
- file,
261
- " fn visit_{method_name}_node(&mut self, node: &{node_variant_name}Node) {{"
262
- )?;
263
- writeln!(file, " visit_{method_name}_node(self, node);")?;
264
- writeln!(file, " }}")?;
265
- }
266
- writeln!(file, "}}")?;
267
- writeln!(file)?;
268
-
269
- // Map C field types (e.g. `rbs_type_name`) to the corresponding
270
- // visitor method name (e.g. `type_name` -> `visit_type_name_node`).
271
- let visitor_method_names: std::collections::HashMap<String, String> = config
272
- .nodes
273
- .iter()
274
- .map(|node| {
275
- let c_type = convert_name(&node.name, CIdentifier::Type);
276
- let c_type = c_type.strip_suffix("_t").unwrap_or(&c_type).to_string();
277
- let method = convert_name(node.variant_name(), CIdentifier::Method);
278
- (c_type, method)
279
- })
280
- .collect();
281
-
282
- let is_visitable = |c_type: &str| -> bool {
283
- matches!(c_type, "rbs_node" | "rbs_node_list" | "rbs_hash")
284
- || visitor_method_names.contains_key(c_type)
285
- };
286
-
287
- for node in &config.nodes {
288
- let node_variant_name = node.variant_name();
289
- let method_name = convert_name(node_variant_name, CIdentifier::Method);
290
-
291
- let has_visitable_fields = node
292
- .fields
293
- .iter()
294
- .flatten()
295
- .any(|field| is_visitable(&field.c_type));
296
-
297
- if !has_visitable_fields {
298
- // If there's nothing to visit in this node, write the empty method with
299
- // underscored parameters, and skip to the next iteration
300
- writeln!(
301
- file,
302
- "pub fn visit_{method_name}_node<V: Visit + ?Sized>(_visitor: &mut V, _node: &{node_variant_name}Node) {{}}"
303
- )?;
304
-
305
- continue;
306
- }
307
-
308
- writeln!(
309
- file,
310
- "pub fn visit_{method_name}_node<V: Visit + ?Sized>(visitor: &mut V, node: &{node_variant_name}Node) {{"
311
- )?;
312
-
313
- if let Some(fields) = &node.fields {
314
- for field in fields {
315
- let field_method_name = if field.name == "type" {
316
- "type_"
317
- } else {
318
- field.name.as_str()
319
- };
320
-
321
- match field.c_type.as_str() {
322
- "rbs_node" => {
323
- if field.optional {
324
- writeln!(
325
- file,
326
- " if let Some(item) = node.{field_method_name}() {{"
327
- )?;
328
- writeln!(file, " visitor.visit(&item);")?;
329
- writeln!(file, " }}")?;
330
- } else {
331
- writeln!(file, " visitor.visit(&node.{field_method_name}());")?;
332
- }
333
- }
334
-
335
- "rbs_node_list" => {
336
- if field.optional {
337
- writeln!(
338
- file,
339
- " if let Some(list) = node.{field_method_name}() {{"
340
- )?;
341
- writeln!(file, " for item in list.iter() {{")?;
342
- writeln!(file, " visitor.visit(&item);")?;
343
- writeln!(file, " }}")?;
344
- writeln!(file, " }}")?;
345
- } else {
346
- writeln!(file, " for item in node.{field_method_name}().iter() {{")?;
347
- writeln!(file, " visitor.visit(&item);")?;
348
- writeln!(file, " }}")?;
349
- }
350
- }
351
-
352
- "rbs_hash" => {
353
- if field.optional {
354
- writeln!(
355
- file,
356
- " if let Some(hash) = node.{field_method_name}() {{"
357
- )?;
358
- writeln!(file, " for (key, value) in hash.iter() {{")?;
359
- writeln!(file, " visitor.visit(&key);")?;
360
- writeln!(file, " visitor.visit(&value);")?;
361
- writeln!(file, " }}")?;
362
- writeln!(file, " }}")?;
363
- } else {
364
- writeln!(
365
- file,
366
- " for (key, value) in node.{field_method_name}().iter() {{"
367
- )?;
368
- writeln!(file, " visitor.visit(&key);")?;
369
- writeln!(file, " visitor.visit(&value);")?;
370
- writeln!(file, " }}")?;
371
- }
372
- }
373
-
374
- _ => {
375
- if let Some(visit_method_name) = visitor_method_names.get(&field.c_type) {
376
- if field.optional {
377
- writeln!(
378
- file,
379
- " if let Some(item) = node.{field_method_name}() {{"
380
- )?;
381
- writeln!(
382
- file,
383
- " visitor.visit_{visit_method_name}_node(&item);"
384
- )?;
385
- writeln!(file, " }}")?;
386
- } else {
387
- writeln!(
388
- file,
389
- " visitor.visit_{visit_method_name}_node(&node.{field_method_name}());"
390
- )?;
391
- }
392
- }
393
- }
394
- }
395
- }
396
- }
397
- writeln!(file, "}}")?;
398
- writeln!(file)?;
399
- }
400
-
401
- Ok(())
402
- }
403
-
404
- fn write_enum_types(
405
- file: &mut File,
406
- enums: &std::collections::HashMap<String, EnumDef>,
407
- ) -> Result<(), Box<dyn Error>> {
408
- for (enum_name, enum_def) in enums {
409
- let rust_enum_name = snake_to_pascal(enum_name);
410
- let c_enum_module = format!("rbs_{}", enum_name);
411
-
412
- // Write enum definition
413
- writeln!(file, "/// Generated from config.yml enums.{}", enum_name)?;
414
- writeln!(file, "#[derive(Debug, Clone, Copy, PartialEq, Eq)]")?;
415
- writeln!(file, "pub enum {} {{", rust_enum_name)?;
416
-
417
- for symbol in &enum_def.symbols {
418
- let variant_name = snake_to_pascal(symbol);
419
- writeln!(file, " /// {} (:{}) ", variant_name, symbol)?;
420
- writeln!(file, " {},", variant_name)?;
421
- }
422
-
423
- writeln!(file, "}}")?;
424
- writeln!(file)?;
425
-
426
- // Write impl block with from_raw
427
- writeln!(file, "impl {} {{", rust_enum_name)?;
428
- writeln!(
429
- file,
430
- " /// Converts the raw C enum value to the Rust enum."
431
- )?;
432
- writeln!(file, " #[must_use]")?;
433
- writeln!(
434
- file,
435
- " pub fn from_raw(raw: {}::Type) -> Self {{",
436
- c_enum_module
437
- )?;
438
- writeln!(file, " match raw {{")?;
439
-
440
- for symbol in &enum_def.symbols {
441
- let variant_name = snake_to_pascal(symbol);
442
- let constant_name = enum_constant_name(enum_name, symbol);
443
- writeln!(
444
- file,
445
- " {}::{} => Self::{},",
446
- c_enum_module, constant_name, variant_name
447
- )?;
448
- }
449
-
450
- writeln!(
451
- file,
452
- " _ => panic!(\"Unknown {}: {{}}\", raw),",
453
- c_enum_module
454
- )?;
455
- writeln!(file, " }}")?;
456
- writeln!(file, " }}")?;
457
- writeln!(file, "}}")?;
458
- writeln!(file)?;
459
- }
460
-
461
- Ok(())
462
- }
463
-
464
- fn generate(config: &Config) -> Result<(), Box<dyn Error>> {
465
- let out_dir = env::var("OUT_DIR")?;
466
- let dest_path = Path::new(&out_dir).join("bindings.rs");
467
-
468
- let mut file = File::create(&dest_path)?;
469
-
470
- writeln!(file, "// Generated by build.rs from config.yml")?;
471
- writeln!(file)?;
472
-
473
- // Generate enum types from config
474
- write_enum_types(&mut file, &config.enums)?;
475
-
476
- for node in &config.nodes {
477
- writeln!(file, "#[derive(Debug)]")?;
478
- writeln!(file, "pub struct {}<'a> {{", node.rust_name)?;
479
- writeln!(file, " #[allow(dead_code)]")?;
480
- writeln!(file, " parser: NonNull<rbs_parser_t>,")?;
481
- writeln!(
482
- file,
483
- " pointer: *mut {},",
484
- convert_name(&node.name, CIdentifier::Type)
485
- )?;
486
- writeln!(
487
- file,
488
- " marker: PhantomData<&'a mut {}>",
489
- convert_name(&node.name, CIdentifier::Type)
490
- )?;
491
- writeln!(file, "}}\n")?;
492
-
493
- writeln!(file, "impl<'a> {}<'a> {{", node.rust_name)?;
494
- writeln!(file, " /// Converts this node to a generic node.")?;
495
- writeln!(file, " #[must_use]")?;
496
- writeln!(file, " pub fn as_node(self) -> Node<'a> {{")?;
497
- writeln!(file, " Node::{}(self)", node.variant_name())?;
498
- writeln!(file, " }}")?;
499
- writeln!(file)?;
500
- writeln!(file, " /// Returns the location of this node.")?;
501
- writeln!(file, " #[must_use]")?;
502
- writeln!(file, " pub fn location(&self) -> RBSLocationRange {{")?;
503
- writeln!(
504
- file,
505
- " RBSLocationRange::new(unsafe {{ (*self.pointer).base.location }})"
506
- )?;
507
- writeln!(file, " }}")?;
508
- writeln!(file)?;
509
-
510
- // Generate location accessor methods
511
- if let Some(locations) = &node.locations {
512
- for location in locations {
513
- let location_name = location.name();
514
- let method_name = format!("{}_location", location_name);
515
- let field_name = format!("{}_range", location_name);
516
-
517
- if location.is_required() {
518
- writeln!(
519
- file,
520
- " /// Returns the `{}` sub-location of this node.",
521
- location_name
522
- )?;
523
- writeln!(file, " #[must_use]")?;
524
- writeln!(
525
- file,
526
- " pub fn {}(&self) -> RBSLocationRange {{",
527
- method_name
528
- )?;
529
- writeln!(
530
- file,
531
- " RBSLocationRange::new(unsafe {{ (*self.pointer).{} }})",
532
- field_name
533
- )?;
534
- writeln!(file, " }}")?;
535
- } else {
536
- writeln!(
537
- file,
538
- " /// Returns the `{}` sub-location of this node if present.",
539
- location_name
540
- )?;
541
- writeln!(file, " #[must_use]")?;
542
- writeln!(
543
- file,
544
- " pub fn {}(&self) -> Option<RBSLocationRange> {{",
545
- method_name
546
- )?;
547
- writeln!(
548
- file,
549
- " let range = unsafe {{ (*self.pointer).{} }};",
550
- field_name
551
- )?;
552
- writeln!(file, " if range.start_char == -1 {{")?;
553
- writeln!(file, " None")?;
554
- writeln!(file, " }} else {{")?;
555
- writeln!(file, " Some(RBSLocationRange::new(range))")?;
556
- writeln!(file, " }}")?;
557
- writeln!(file, " }}")?;
558
- }
559
- writeln!(file)?;
560
- }
561
- }
562
-
563
- if let Some(fields) = &node.fields {
564
- for field in fields {
565
- match field.c_type.as_str() {
566
- "rbs_string" => {
567
- writeln!(file, " #[must_use]")?;
568
- writeln!(file, " pub fn {}(&self) -> RBSString {{", field.name)?;
569
- writeln!(
570
- file,
571
- " RBSString::new(unsafe {{ &(*self.pointer).{} }})",
572
- field.c_name()
573
- )?;
574
- writeln!(file, " }}")?;
575
- writeln!(file)?;
576
- }
577
- "bool" => {
578
- writeln!(file, " #[must_use]")?;
579
- writeln!(file, " pub fn {}(&self) -> bool {{", field.name)?;
580
- writeln!(file, " unsafe {{ (*self.pointer).{} }}", field.name)?;
581
- writeln!(file, " }}")?;
582
- writeln!(file)?;
583
- }
584
- "rbs_ast_comment" => {
585
- write_node_field_accessor(&mut file, field, "CommentNode")?
586
- }
587
- "rbs_ast_declarations_class_super" => {
588
- write_node_field_accessor(&mut file, field, "ClassSuperNode")?
589
- }
590
- "rbs_ast_symbol" => write_node_field_accessor(&mut file, field, "SymbolNode")?,
591
- "rbs_hash" => {
592
- write_node_field_accessor(&mut file, field, "RBSHash")?;
593
- }
594
- "rbs_namespace" => {
595
- write_node_field_accessor(&mut file, field, "NamespaceNode")?;
596
- }
597
- "rbs_node" => {
598
- let name = if field.name == "type" {
599
- "type_"
600
- } else {
601
- field.name.as_str()
602
- };
603
- if field.optional {
604
- writeln!(file, " #[must_use]")?;
605
- writeln!(file, " pub fn {name}(&self) -> Option<Node<'a>> {{")?;
606
- writeln!(
607
- file,
608
- " let ptr = unsafe {{ (*self.pointer).{} }};",
609
- field.c_name()
610
- )?;
611
- writeln!(
612
- file,
613
- " if ptr.is_null() {{ None }} else {{ Some(Node::new(self.parser, ptr)) }}"
614
- )?;
615
- } else {
616
- writeln!(file, " #[must_use]")?;
617
- writeln!(file, " pub fn {name}(&self) -> Node<'a> {{")?;
618
- writeln!(
619
- file,
620
- " unsafe {{ Node::new(self.parser, (*self.pointer).{}) }}",
621
- field.c_name()
622
- )?;
623
- }
624
- writeln!(file, " }}")?;
625
- writeln!(file)?;
626
- }
627
- "rbs_node_list" => {
628
- write_node_field_accessor(&mut file, field, "NodeList")?;
629
- }
630
- "rbs_type_name" => {
631
- write_node_field_accessor(&mut file, field, "TypeNameNode")?;
632
- }
633
- "rbs_types_block" => {
634
- write_node_field_accessor(&mut file, field, "BlockTypeNode")?
635
- }
636
- c_type if config.enums.contains_key(c_type) => {
637
- write_enum_field_accessor(&mut file, field, c_type)?;
638
- }
639
- "rbs_attr_ivar_name" => {
640
- writeln!(file, " #[must_use]")?;
641
- writeln!(file, " pub fn {}(&self) -> AttrIvarName {{", field.name)?;
642
- writeln!(
643
- file,
644
- " AttrIvarName::from_raw(unsafe {{ (*self.pointer).{} }})",
645
- field.c_name()
646
- )?;
647
- writeln!(file, " }}")?;
648
- writeln!(file)?;
649
- }
650
- "rbs_location_range" => {
651
- if field.optional {
652
- writeln!(file, " #[must_use]")?;
653
- writeln!(
654
- file,
655
- " pub fn {}(&self) -> Option<RBSLocationRange> {{",
656
- field.name
657
- )?;
658
- writeln!(
659
- file,
660
- " let range = unsafe {{ (*self.pointer).{} }};",
661
- field.c_name()
662
- )?;
663
- writeln!(file, " if range.start_char == -1 {{")?;
664
- writeln!(file, " None")?;
665
- writeln!(file, " }} else {{")?;
666
- writeln!(file, " Some(RBSLocationRange::new(range))")?;
667
- writeln!(file, " }}")?;
668
- writeln!(file, " }}")?;
669
- } else {
670
- writeln!(file, " #[must_use]")?;
671
- writeln!(
672
- file,
673
- " pub fn {}(&self) -> RBSLocationRange {{",
674
- field.name
675
- )?;
676
- writeln!(
677
- file,
678
- " RBSLocationRange::new(unsafe {{ (*self.pointer).{} }})",
679
- field.c_name()
680
- )?;
681
- writeln!(file, " }}")?;
682
- }
683
- writeln!(file)?;
684
- }
685
- "rbs_location_range_list" => {
686
- writeln!(file, " #[must_use]")?;
687
- writeln!(
688
- file,
689
- " pub fn {}(&self) -> RBSLocationRangeList<'a> {{",
690
- field.name
691
- )?;
692
- writeln!(
693
- file,
694
- " RBSLocationRangeList {{ parser: self.parser, pointer: unsafe {{ (*self.pointer).{} }}, marker: PhantomData }}",
695
- field.c_name()
696
- )?;
697
- writeln!(file, " }}")?;
698
- writeln!(file)?;
699
- }
700
- _ => panic!("Unknown field type: {}", field.c_type),
701
- }
702
- }
703
- }
704
- writeln!(file, "}}\n")?;
705
- }
706
-
707
- // Generate the Node enum to wrap all of the structs
708
- writeln!(file, "#[derive(Debug)]")?;
709
- writeln!(file, "pub enum Node<'a> {{")?;
710
- for node in &config.nodes {
711
- let variant_name = node
712
- .rust_name
713
- .strip_suffix("Node")
714
- .unwrap_or(&node.rust_name);
715
-
716
- writeln!(file, " {variant_name}({}<'a>),", node.rust_name)?;
717
- }
718
- writeln!(file, "}}")?;
719
-
720
- writeln!(file, "impl Node<'_> {{")?;
721
- writeln!(file, " #[allow(clippy::missing_safety_doc)]")?;
722
- writeln!(
723
- file,
724
- " fn new(parser: NonNull<rbs_parser_t>, node: *mut rbs_node_t) -> Self {{"
725
- )?;
726
- writeln!(file, " match unsafe {{ (*node).type_ }} {{")?;
727
- for node in &config.nodes {
728
- let enum_name = convert_name(&node.name, CIdentifier::Constant);
729
- let c_type = convert_name(&node.name, CIdentifier::Type);
730
-
731
- writeln!(
732
- file,
733
- " rbs_node_type::{enum_name} => Self::{}({} {{ parser, pointer: node.cast::<{c_type}>(), marker: PhantomData }}),",
734
- node.variant_name(),
735
- node.rust_name,
736
- )?;
737
- }
738
- writeln!(
739
- file,
740
- " _ => panic!(\"Unknown node type: {{}}\", unsafe {{ (*node).type_ }})"
741
- )?;
742
- writeln!(file, " }}")?;
743
- writeln!(file, " }}")?;
744
- writeln!(file)?;
745
- writeln!(file, " /// Returns the location of the entire node.")?;
746
- writeln!(file, " #[must_use]")?;
747
- writeln!(file, " pub fn location(&self) -> RBSLocationRange {{")?;
748
- writeln!(file, " match self {{")?;
749
- for node in &config.nodes {
750
- writeln!(
751
- file,
752
- " Node::{}(node) => node.location(),",
753
- node.variant_name()
754
- )?;
755
- }
756
- writeln!(file, " }}")?;
757
- writeln!(file, " }}")?;
758
- writeln!(file, "}}")?;
759
- writeln!(file)?;
760
-
761
- write_visit_trait(&mut file, config)?;
762
-
763
- Ok(())
764
- }