rubydex 0.1.0.beta12-aarch64-linux

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.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +23 -0
  3. data/README.md +125 -0
  4. data/THIRD_PARTY_LICENSES.html +4562 -0
  5. data/exe/rdx +47 -0
  6. data/ext/rubydex/declaration.c +453 -0
  7. data/ext/rubydex/declaration.h +23 -0
  8. data/ext/rubydex/definition.c +284 -0
  9. data/ext/rubydex/definition.h +28 -0
  10. data/ext/rubydex/diagnostic.c +6 -0
  11. data/ext/rubydex/diagnostic.h +11 -0
  12. data/ext/rubydex/document.c +97 -0
  13. data/ext/rubydex/document.h +10 -0
  14. data/ext/rubydex/extconf.rb +138 -0
  15. data/ext/rubydex/graph.c +681 -0
  16. data/ext/rubydex/graph.h +10 -0
  17. data/ext/rubydex/handle.h +44 -0
  18. data/ext/rubydex/location.c +22 -0
  19. data/ext/rubydex/location.h +15 -0
  20. data/ext/rubydex/reference.c +123 -0
  21. data/ext/rubydex/reference.h +15 -0
  22. data/ext/rubydex/rubydex.c +22 -0
  23. data/ext/rubydex/utils.c +108 -0
  24. data/ext/rubydex/utils.h +34 -0
  25. data/lib/rubydex/3.2/rubydex.so +0 -0
  26. data/lib/rubydex/3.3/rubydex.so +0 -0
  27. data/lib/rubydex/3.4/rubydex.so +0 -0
  28. data/lib/rubydex/4.0/rubydex.so +0 -0
  29. data/lib/rubydex/comment.rb +17 -0
  30. data/lib/rubydex/diagnostic.rb +21 -0
  31. data/lib/rubydex/failures.rb +15 -0
  32. data/lib/rubydex/graph.rb +98 -0
  33. data/lib/rubydex/keyword.rb +17 -0
  34. data/lib/rubydex/keyword_parameter.rb +13 -0
  35. data/lib/rubydex/librubydex_sys.so +0 -0
  36. data/lib/rubydex/location.rb +90 -0
  37. data/lib/rubydex/mixin.rb +22 -0
  38. data/lib/rubydex/version.rb +5 -0
  39. data/lib/rubydex.rb +23 -0
  40. data/rbi/rubydex.rbi +422 -0
  41. data/rust/Cargo.lock +1851 -0
  42. data/rust/Cargo.toml +29 -0
  43. data/rust/about.hbs +78 -0
  44. data/rust/about.toml +10 -0
  45. data/rust/rubydex/Cargo.toml +42 -0
  46. data/rust/rubydex/src/compile_assertions.rs +13 -0
  47. data/rust/rubydex/src/diagnostic.rs +110 -0
  48. data/rust/rubydex/src/errors.rs +28 -0
  49. data/rust/rubydex/src/indexing/local_graph.rs +224 -0
  50. data/rust/rubydex/src/indexing/rbs_indexer.rs +1551 -0
  51. data/rust/rubydex/src/indexing/ruby_indexer.rs +2329 -0
  52. data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +4962 -0
  53. data/rust/rubydex/src/indexing.rs +210 -0
  54. data/rust/rubydex/src/integrity.rs +279 -0
  55. data/rust/rubydex/src/job_queue.rs +205 -0
  56. data/rust/rubydex/src/lib.rs +17 -0
  57. data/rust/rubydex/src/listing.rs +371 -0
  58. data/rust/rubydex/src/main.rs +160 -0
  59. data/rust/rubydex/src/model/built_in.rs +83 -0
  60. data/rust/rubydex/src/model/comment.rs +24 -0
  61. data/rust/rubydex/src/model/declaration.rs +671 -0
  62. data/rust/rubydex/src/model/definitions.rs +1682 -0
  63. data/rust/rubydex/src/model/document.rs +222 -0
  64. data/rust/rubydex/src/model/encoding.rs +22 -0
  65. data/rust/rubydex/src/model/graph.rs +3754 -0
  66. data/rust/rubydex/src/model/id.rs +110 -0
  67. data/rust/rubydex/src/model/identity_maps.rs +58 -0
  68. data/rust/rubydex/src/model/ids.rs +60 -0
  69. data/rust/rubydex/src/model/keywords.rs +256 -0
  70. data/rust/rubydex/src/model/name.rs +298 -0
  71. data/rust/rubydex/src/model/references.rs +111 -0
  72. data/rust/rubydex/src/model/string_ref.rs +50 -0
  73. data/rust/rubydex/src/model/visibility.rs +41 -0
  74. data/rust/rubydex/src/model.rs +15 -0
  75. data/rust/rubydex/src/offset.rs +147 -0
  76. data/rust/rubydex/src/position.rs +6 -0
  77. data/rust/rubydex/src/query.rs +1841 -0
  78. data/rust/rubydex/src/resolution.rs +6517 -0
  79. data/rust/rubydex/src/stats/memory.rs +71 -0
  80. data/rust/rubydex/src/stats/orphan_report.rs +264 -0
  81. data/rust/rubydex/src/stats/timer.rs +127 -0
  82. data/rust/rubydex/src/stats.rs +11 -0
  83. data/rust/rubydex/src/test_utils/context.rs +226 -0
  84. data/rust/rubydex/src/test_utils/graph_test.rs +730 -0
  85. data/rust/rubydex/src/test_utils/local_graph_test.rs +602 -0
  86. data/rust/rubydex/src/test_utils.rs +52 -0
  87. data/rust/rubydex/src/visualization/dot.rs +192 -0
  88. data/rust/rubydex/src/visualization.rs +6 -0
  89. data/rust/rubydex/tests/cli.rs +185 -0
  90. data/rust/rubydex-mcp/Cargo.toml +28 -0
  91. data/rust/rubydex-mcp/src/main.rs +48 -0
  92. data/rust/rubydex-mcp/src/server.rs +1145 -0
  93. data/rust/rubydex-mcp/src/tools.rs +49 -0
  94. data/rust/rubydex-mcp/tests/mcp.rs +302 -0
  95. data/rust/rubydex-sys/Cargo.toml +20 -0
  96. data/rust/rubydex-sys/build.rs +14 -0
  97. data/rust/rubydex-sys/cbindgen.toml +12 -0
  98. data/rust/rubydex-sys/src/declaration_api.rs +485 -0
  99. data/rust/rubydex-sys/src/definition_api.rs +443 -0
  100. data/rust/rubydex-sys/src/diagnostic_api.rs +99 -0
  101. data/rust/rubydex-sys/src/document_api.rs +85 -0
  102. data/rust/rubydex-sys/src/graph_api.rs +948 -0
  103. data/rust/rubydex-sys/src/lib.rs +79 -0
  104. data/rust/rubydex-sys/src/location_api.rs +79 -0
  105. data/rust/rubydex-sys/src/name_api.rs +135 -0
  106. data/rust/rubydex-sys/src/reference_api.rs +267 -0
  107. data/rust/rubydex-sys/src/utils.rs +70 -0
  108. data/rust/rustfmt.toml +2 -0
  109. metadata +159 -0
@@ -0,0 +1,443 @@
1
+ //! This file provides the C API for Definition accessors
2
+
3
+ use crate::graph_api::{GraphPointer, with_graph};
4
+ use crate::location_api::{Location, create_location_for_uri_and_offset};
5
+ use crate::reference_api::CConstantReference;
6
+ use libc::c_char;
7
+ use rubydex::model::definitions::{Definition, Mixin};
8
+ use rubydex::model::ids::DefinitionId;
9
+ use std::ffi::CString;
10
+ use std::ptr;
11
+
12
+ /// C-compatible enum representing the kind of a definition.
13
+ #[repr(C)]
14
+ #[derive(Copy, Clone, Debug, PartialEq, Eq)]
15
+ pub enum DefinitionKind {
16
+ Class = 0,
17
+ SingletonClass = 1,
18
+ Module = 2,
19
+ Constant = 3,
20
+ ConstantAlias = 4,
21
+ ConstantVisibility = 5,
22
+ MethodVisibility = 6,
23
+ Method = 7,
24
+ AttrAccessor = 8,
25
+ AttrReader = 9,
26
+ AttrWriter = 10,
27
+ GlobalVariable = 11,
28
+ InstanceVariable = 12,
29
+ ClassVariable = 13,
30
+ MethodAlias = 14,
31
+ GlobalVariableAlias = 15,
32
+ }
33
+
34
+ #[repr(C)]
35
+ #[derive(Debug, Clone, Copy)]
36
+ pub struct CDefinition {
37
+ pub id: u64,
38
+ pub kind: DefinitionKind,
39
+ }
40
+
41
+ pub(crate) fn map_definition_to_kind(defn: &Definition) -> DefinitionKind {
42
+ match defn {
43
+ Definition::Class(_) => DefinitionKind::Class,
44
+ Definition::SingletonClass(_) => DefinitionKind::SingletonClass,
45
+ Definition::Module(_) => DefinitionKind::Module,
46
+ Definition::Constant(_) => DefinitionKind::Constant,
47
+ Definition::ConstantAlias(_) => DefinitionKind::ConstantAlias,
48
+ Definition::ConstantVisibility(_) => DefinitionKind::ConstantVisibility,
49
+ Definition::MethodVisibility(_) => DefinitionKind::MethodVisibility,
50
+ Definition::Method(_) => DefinitionKind::Method,
51
+ Definition::AttrAccessor(_) => DefinitionKind::AttrAccessor,
52
+ Definition::AttrReader(_) => DefinitionKind::AttrReader,
53
+ Definition::AttrWriter(_) => DefinitionKind::AttrWriter,
54
+ Definition::GlobalVariable(_) => DefinitionKind::GlobalVariable,
55
+ Definition::InstanceVariable(_) => DefinitionKind::InstanceVariable,
56
+ Definition::ClassVariable(_) => DefinitionKind::ClassVariable,
57
+ Definition::MethodAlias(_) => DefinitionKind::MethodAlias,
58
+ Definition::GlobalVariableAlias(_) => DefinitionKind::GlobalVariableAlias,
59
+ }
60
+ }
61
+
62
+ /// Returns the enum kind for a definition id (e.g. Class, Module).
63
+ ///
64
+ /// # Safety
65
+ ///
66
+ /// Assumes pointer is valid.
67
+ ///
68
+ /// # Panics
69
+ ///
70
+ /// This function will panic if the definition cannot be found.
71
+ #[unsafe(no_mangle)]
72
+ pub unsafe extern "C" fn rdx_definition_kind(pointer: GraphPointer, definition_id: u64) -> DefinitionKind {
73
+ with_graph(pointer, |graph| {
74
+ let definition_id = DefinitionId::new(definition_id);
75
+ if let Some(defn) = graph.definitions().get(&definition_id) {
76
+ map_definition_to_kind(defn)
77
+ } else {
78
+ panic!("Definition not found: {definition_id:?}");
79
+ }
80
+ })
81
+ }
82
+
83
+ /// Returns the UTF-8 unqualified name string for a definition id.
84
+ /// Caller must free with `free_c_string`.
85
+ ///
86
+ /// # Safety
87
+ ///
88
+ /// Assumes pointer is valid.
89
+ ///
90
+ /// # Panics
91
+ ///
92
+ /// This function will panic if the definition cannot be found.
93
+ #[unsafe(no_mangle)]
94
+ pub unsafe extern "C" fn rdx_definition_name(pointer: GraphPointer, definition_id: u64) -> *const c_char {
95
+ with_graph(pointer, |graph| {
96
+ let def_id = DefinitionId::new(definition_id);
97
+ if let Some(defn) = graph.definitions().get(&def_id) {
98
+ let string_id = graph.definition_string_id(defn);
99
+
100
+ if let Some(name) = graph.strings().get(&string_id) {
101
+ CString::new(name.as_str()).unwrap().into_raw().cast_const()
102
+ } else {
103
+ ptr::null()
104
+ }
105
+ } else {
106
+ ptr::null()
107
+ }
108
+ })
109
+ }
110
+
111
+ /// Shared iterator over definition (id, kind) pairs
112
+ #[derive(Debug)]
113
+ pub struct DefinitionsIter {
114
+ entries: Box<[CDefinition]>,
115
+ index: usize,
116
+ }
117
+
118
+ iterator!(DefinitionsIter, entries: CDefinition);
119
+
120
+ /// # Safety
121
+ /// `iter` must be a valid pointer previously returned by `DefinitionsIter::new`.
122
+ #[unsafe(no_mangle)]
123
+ pub unsafe extern "C" fn rdx_definitions_iter_len(iter: *const DefinitionsIter) -> usize {
124
+ unsafe { DefinitionsIter::len(iter) }
125
+ }
126
+
127
+ /// # Safety
128
+ /// - `iter` must be a valid pointer previously returned by `DefinitionsIter::new`.
129
+ /// - `out` must be a valid, writable pointer.
130
+ #[unsafe(no_mangle)]
131
+ pub unsafe extern "C" fn rdx_definitions_iter_next(iter: *mut DefinitionsIter, out: *mut CDefinition) -> bool {
132
+ unsafe { DefinitionsIter::next(iter, out) }
133
+ }
134
+
135
+ /// # Safety
136
+ /// - `iter` must be a pointer previously returned by `DefinitionsIter::new`.
137
+ /// - `iter` must not be used after being freed.
138
+ #[unsafe(no_mangle)]
139
+ pub unsafe extern "C" fn rdx_definitions_iter_free(iter: *mut DefinitionsIter) {
140
+ unsafe { DefinitionsIter::free(iter) }
141
+ }
142
+
143
+ /// C-compatible struct representing a single comment with its string and location
144
+ #[repr(C)]
145
+ pub struct CommentEntry {
146
+ pub string: *const c_char,
147
+ pub location: *mut Location,
148
+ }
149
+
150
+ /// C-compatible array of comments
151
+ #[repr(C)]
152
+ pub struct CommentArray {
153
+ pub items: *mut CommentEntry,
154
+ pub len: usize,
155
+ }
156
+
157
+ /// Returns a newly allocated array of comments (string and location) for the given definition id.
158
+ /// Caller must free the returned pointer with `rdx_definition_comments_free` and each inner string with `free_c_string` if needed.
159
+ ///
160
+ /// # Safety
161
+ /// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
162
+ /// - `definition_id` must be a valid definition id.
163
+ ///
164
+ /// # Panics
165
+ /// This function will panic if a definition or document cannot be found.
166
+ #[unsafe(no_mangle)]
167
+ pub unsafe extern "C" fn rdx_definition_comments(pointer: GraphPointer, definition_id: u64) -> *mut CommentArray {
168
+ with_graph(pointer, |graph| {
169
+ let def_id = DefinitionId::new(definition_id);
170
+ let Some(defn) = graph.definitions().get(&def_id) else {
171
+ panic!("Definition not found: {definition_id:?}");
172
+ };
173
+
174
+ let uri_id = *defn.uri_id();
175
+ let document = graph.documents().get(&uri_id).expect("document should exist");
176
+
177
+ let mut entries = defn
178
+ .comments()
179
+ .iter()
180
+ .map(|c| CommentEntry {
181
+ string: CString::new(c.string().as_str()).unwrap().into_raw().cast_const(),
182
+ location: create_location_for_uri_and_offset(graph, document, c.offset()),
183
+ })
184
+ .collect::<Vec<CommentEntry>>()
185
+ .into_boxed_slice();
186
+
187
+ let len = entries.len();
188
+ let items_ptr = entries.as_mut_ptr();
189
+ std::mem::forget(entries);
190
+
191
+ Box::into_raw(Box::new(CommentArray { items: items_ptr, len }))
192
+ })
193
+ }
194
+
195
+ /// Frees a `CommentArray` previously returned by `rdx_definition_comments`.
196
+ ///
197
+ /// # Safety
198
+ /// - `ptr` must be a valid pointer previously returned by `rdx_definition_comments`.
199
+ /// - `ptr` must not be used after being freed.
200
+ #[unsafe(no_mangle)]
201
+ pub unsafe extern "C" fn rdx_definition_comments_free(ptr: *mut CommentArray) {
202
+ if ptr.is_null() {
203
+ return;
204
+ }
205
+
206
+ // Take ownership of the CommentArray
207
+ let arr = unsafe { Box::from_raw(ptr) };
208
+
209
+ if !arr.items.is_null() && arr.len > 0 {
210
+ // Reconstruct the boxed slice so we can drop it after freeing inner allocations
211
+ let slice_ptr = ptr::slice_from_raw_parts_mut(arr.items, arr.len);
212
+ let mut boxed_slice: Box<[CommentEntry]> = unsafe { Box::from_raw(slice_ptr) };
213
+
214
+ for item in &mut boxed_slice {
215
+ if !item.string.is_null() {
216
+ // Free the CString allocated for the comment string
217
+ let _ = unsafe { CString::from_raw(item.string.cast_mut()) };
218
+ }
219
+ if !item.location.is_null() {
220
+ unsafe { crate::location_api::rdx_location_free(item.location) };
221
+ item.location = ptr::null_mut();
222
+ }
223
+ }
224
+
225
+ // boxed_slice is dropped here, freeing the items buffer
226
+ }
227
+ // arr is dropped here
228
+ }
229
+
230
+ /// Returns a newly allocated `Location` for the given definition id.
231
+ /// Caller must free the returned pointer with `rdx_location_free`.
232
+ ///
233
+ /// # Safety
234
+ /// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
235
+ /// - `definition_id` must be a valid definition id.
236
+ ///
237
+ /// # Panics
238
+ ///
239
+ /// This function will panic if a definition or document cannot be found.
240
+ #[unsafe(no_mangle)]
241
+ pub unsafe extern "C" fn rdx_definition_location(pointer: GraphPointer, definition_id: u64) -> *mut Location {
242
+ with_graph(pointer, |graph| {
243
+ let def_id = DefinitionId::new(definition_id);
244
+ let Some(defn) = graph.definitions().get(&def_id) else {
245
+ panic!("Definition not found: {definition_id:?}");
246
+ };
247
+
248
+ let document = graph.documents().get(defn.uri_id()).expect("document should exist");
249
+ create_location_for_uri_and_offset(graph, document, defn.offset())
250
+ })
251
+ }
252
+
253
+ /// Creates a new iterator over definition IDs for a given declaration by snapshotting the current set of IDs.
254
+ ///
255
+ /// # Panics
256
+ ///
257
+ /// This function will panic if a definition cannot be found.
258
+ pub(crate) fn rdx_definitions_iter_new_from_ids<'a, I>(
259
+ graph: &rubydex::model::graph::Graph,
260
+ ids: I,
261
+ ) -> *mut DefinitionsIter
262
+ where
263
+ I: IntoIterator<Item = &'a DefinitionId>,
264
+ {
265
+ let entries = ids
266
+ .into_iter()
267
+ .map(|def_id| {
268
+ let id = **def_id;
269
+ let kind = graph
270
+ .definitions()
271
+ .get(&DefinitionId::new(id))
272
+ .map_or_else(|| panic!("Definition not found: {id:?}"), map_definition_to_kind);
273
+ CDefinition { id, kind }
274
+ })
275
+ .collect::<Vec<_>>()
276
+ .into_boxed_slice();
277
+
278
+ DefinitionsIter::new(entries)
279
+ }
280
+
281
+ /// Returns true if the definition is deprecated.
282
+ ///
283
+ /// # Safety
284
+ /// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
285
+ /// - `definition_id` must be a valid definition id.
286
+ ///
287
+ /// # Panics
288
+ /// This function will panic if a definition cannot be found.
289
+ #[unsafe(no_mangle)]
290
+ pub unsafe extern "C" fn rdx_definition_is_deprecated(pointer: GraphPointer, definition_id: u64) -> bool {
291
+ with_graph(pointer, |graph| {
292
+ let def_id = DefinitionId::new(definition_id);
293
+ let defn = graph.definitions().get(&def_id).expect("definition not found");
294
+ defn.is_deprecated()
295
+ })
296
+ }
297
+
298
+ /// Returns a newly allocated `Location` for the name portion of a definition id.
299
+ /// For class, module, and singleton class definitions, this returns the location of just
300
+ /// the name (e.g., "Bar" in `class Foo::Bar`).
301
+ /// For other definition types, returns NULL.
302
+ /// Caller must free the returned pointer with `rdx_location_free`.
303
+ ///
304
+ /// # Safety
305
+ /// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
306
+ /// - `definition_id` must be a valid definition id.
307
+ ///
308
+ /// # Panics
309
+ /// Panics if the definition's document does not exist in the graph.
310
+ #[unsafe(no_mangle)]
311
+ pub unsafe extern "C" fn rdx_definition_name_location(pointer: GraphPointer, definition_id: u64) -> *mut Location {
312
+ with_graph(pointer, |graph| {
313
+ let def_id = DefinitionId::new(definition_id);
314
+ let Some(defn) = graph.definitions().get(&def_id) else {
315
+ return ptr::null_mut();
316
+ };
317
+ let Some(name_offset) = defn.name_offset() else {
318
+ return ptr::null_mut();
319
+ };
320
+ let document = graph.documents().get(defn.uri_id()).expect("document should exist");
321
+ create_location_for_uri_and_offset(graph, document, name_offset)
322
+ })
323
+ }
324
+
325
+ /// Returns the superclass constant reference for a class definition, or NULL if the class has no superclass. Caller
326
+ /// must free with `free_c_constant_reference`.
327
+ ///
328
+ /// # Safety
329
+ /// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
330
+ /// - `definition_id` must be a valid definition id for a class definition.
331
+ ///
332
+ /// # Panics
333
+ /// This function will panic if the definition cannot be found or is not a class definition.
334
+ #[unsafe(no_mangle)]
335
+ pub unsafe extern "C" fn rdx_class_definition_superclass(
336
+ pointer: GraphPointer,
337
+ definition_id: u64,
338
+ ) -> *const CConstantReference {
339
+ with_graph(pointer, |graph| {
340
+ let def_id = DefinitionId::new(definition_id);
341
+ let defn = graph.definitions().get(&def_id).expect("Definition not found");
342
+
343
+ let Definition::Class(class_def) = defn else {
344
+ panic!("Definition is not a class: {definition_id}");
345
+ };
346
+
347
+ let Some(ref_id) = class_def.superclass_ref() else {
348
+ return ptr::null();
349
+ };
350
+
351
+ Box::into_raw(Box::new(CConstantReference::from_id(graph, *ref_id))).cast_const()
352
+ })
353
+ }
354
+
355
+ /// C-compatible enum representing the kind of a mixin.
356
+ #[repr(C)]
357
+ #[derive(Debug, Clone, Copy)]
358
+ pub enum MixinKind {
359
+ Include = 0,
360
+ Prepend = 1,
361
+ Extend = 2,
362
+ }
363
+
364
+ /// C-compatible struct representing a mixin (kind + constant reference).
365
+ #[repr(C)]
366
+ #[derive(Debug, Clone, Copy)]
367
+ pub struct CMixin {
368
+ pub kind: MixinKind,
369
+ pub constant_reference: CConstantReference,
370
+ }
371
+
372
+ #[derive(Debug)]
373
+ pub struct MixinsIter {
374
+ entries: Box<[CMixin]>,
375
+ index: usize,
376
+ }
377
+
378
+ iterator!(MixinsIter, entries: CMixin);
379
+
380
+ /// # Safety
381
+ /// `iter` must be a valid pointer previously returned by `rdx_definition_mixins`.
382
+ #[unsafe(no_mangle)]
383
+ pub unsafe extern "C" fn rdx_mixins_iter_len(iter: *const MixinsIter) -> usize {
384
+ unsafe { MixinsIter::len(iter) }
385
+ }
386
+
387
+ /// # Safety
388
+ /// - `iter` must be a valid pointer previously returned by `rdx_definition_mixins`.
389
+ /// - `out` must be a valid, writable pointer.
390
+ #[unsafe(no_mangle)]
391
+ pub unsafe extern "C" fn rdx_mixins_iter_next(iter: *mut MixinsIter, out: *mut CMixin) -> bool {
392
+ unsafe { MixinsIter::next(iter, out) }
393
+ }
394
+
395
+ /// # Safety
396
+ /// - `iter` must be a pointer previously returned by `rdx_definition_mixins`.
397
+ /// - `iter` must not be used after being freed.
398
+ #[unsafe(no_mangle)]
399
+ pub unsafe extern "C" fn rdx_mixins_iter_free(iter: *mut MixinsIter) {
400
+ unsafe { MixinsIter::free(iter) }
401
+ }
402
+
403
+ fn map_mixin_kind(mixin: &Mixin) -> MixinKind {
404
+ match mixin {
405
+ Mixin::Include(_) => MixinKind::Include,
406
+ Mixin::Prepend(_) => MixinKind::Prepend,
407
+ Mixin::Extend(_) => MixinKind::Extend,
408
+ }
409
+ }
410
+
411
+ /// Returns an iterator over the mixins for a definition (class, module, or singleton class).
412
+ /// Returns NULL for definition types that do not support mixins.
413
+ ///
414
+ /// # Safety
415
+ /// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
416
+ /// - `definition_id` must be a valid definition id.
417
+ ///
418
+ /// # Panics
419
+ /// This function will panic if the definition cannot be found.
420
+ #[unsafe(no_mangle)]
421
+ pub unsafe extern "C" fn rdx_definition_mixins(pointer: GraphPointer, definition_id: u64) -> *mut MixinsIter {
422
+ with_graph(pointer, |graph| {
423
+ let def_id = DefinitionId::new(definition_id);
424
+ let defn = graph.definitions().get(&def_id).expect("Definition not found");
425
+
426
+ let mixins = match defn {
427
+ Definition::Class(class_def) => class_def.mixins(),
428
+ Definition::Module(mod_def) => mod_def.mixins(),
429
+ Definition::SingletonClass(singleton_def) => singleton_def.mixins(),
430
+ _ => return ptr::null_mut(),
431
+ };
432
+
433
+ let entries: Vec<CMixin> = mixins
434
+ .iter()
435
+ .map(|mixin| CMixin {
436
+ kind: map_mixin_kind(mixin),
437
+ constant_reference: CConstantReference::from_id(graph, *mixin.constant_reference_id()),
438
+ })
439
+ .collect();
440
+
441
+ MixinsIter::new(entries.into_boxed_slice())
442
+ })
443
+ }
@@ -0,0 +1,99 @@
1
+ //! Diagnostics-related FFI helpers
2
+
3
+ use crate::graph_api::{GraphPointer, with_graph};
4
+ use crate::location_api::{Location, create_location_for_uri_and_offset};
5
+ use libc::c_char;
6
+ use std::{ffi::CString, mem, ptr};
7
+
8
+ /// C-compatible struct representing a diagnostic entry.
9
+ #[repr(C)]
10
+ pub struct DiagnosticEntry {
11
+ pub rule: *const c_char,
12
+ pub message: *const c_char,
13
+ pub location: *mut Location,
14
+ }
15
+
16
+ /// C-compatible array wrapper for diagnostics.
17
+ #[repr(C)]
18
+ pub struct DiagnosticArray {
19
+ pub items: *mut DiagnosticEntry,
20
+ pub len: usize,
21
+ }
22
+
23
+ impl DiagnosticArray {
24
+ fn from_vec(mut entries: Vec<DiagnosticEntry>) -> *mut DiagnosticArray {
25
+ let len = entries.len();
26
+ let ptr = entries.as_mut_ptr();
27
+ mem::forget(entries);
28
+ Box::into_raw(Box::new(DiagnosticArray { items: ptr, len }))
29
+ }
30
+ }
31
+
32
+ /// Returns all diagnostics currently recorded in the global graph.
33
+ ///
34
+ /// # Safety
35
+ ///
36
+ /// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
37
+ /// - The pointed graph must remain alive for the duration of the call.
38
+ ///
39
+ /// # Panics
40
+ ///
41
+ /// - If a diagnostic references a URI whose file cannot be read to build a location.
42
+ #[unsafe(no_mangle)]
43
+ pub unsafe extern "C" fn rdx_graph_diagnostics(pointer: GraphPointer) -> *mut DiagnosticArray {
44
+ with_graph(pointer, |graph| {
45
+ let entries = graph
46
+ .all_diagnostics()
47
+ .iter()
48
+ .map(|diagnostic| {
49
+ let document = graph.documents().get(diagnostic.uri_id()).unwrap();
50
+ let location = create_location_for_uri_and_offset(graph, document, diagnostic.offset());
51
+
52
+ DiagnosticEntry {
53
+ rule: CString::new(diagnostic.rule().to_string())
54
+ .unwrap()
55
+ .into_raw()
56
+ .cast_const(),
57
+ message: CString::new(diagnostic.message()).unwrap().into_raw().cast_const(),
58
+ location,
59
+ }
60
+ })
61
+ .collect::<Vec<DiagnosticEntry>>();
62
+
63
+ DiagnosticArray::from_vec(entries)
64
+ })
65
+ }
66
+
67
+ /// Frees a diagnostic array previously returned by `rdx_graph_diagnostics`.
68
+ ///
69
+ /// # Safety
70
+ ///
71
+ /// - `ptr` must be a valid pointer previously returned by `rdx_graph_diagnostics`.
72
+ /// - `ptr` must not be used after being freed.
73
+ #[unsafe(no_mangle)]
74
+ pub unsafe extern "C" fn rdx_diagnostics_free(ptr: *mut DiagnosticArray) {
75
+ if ptr.is_null() {
76
+ return;
77
+ }
78
+
79
+ let array = unsafe { Box::from_raw(ptr) };
80
+ if !array.items.is_null() && array.len > 0 {
81
+ let slice_ptr = ptr::slice_from_raw_parts_mut(array.items, array.len);
82
+ let mut boxed_slice: Box<[DiagnosticEntry]> = unsafe { Box::from_raw(slice_ptr) };
83
+
84
+ for entry in &mut *boxed_slice {
85
+ if !entry.rule.is_null() {
86
+ let _ = unsafe { CString::from_raw(entry.rule.cast_mut()) };
87
+ }
88
+ if !entry.message.is_null() {
89
+ let _ = unsafe { CString::from_raw(entry.message.cast_mut()) };
90
+ }
91
+ if !entry.location.is_null() {
92
+ unsafe { crate::location_api::rdx_location_free(entry.location) };
93
+ entry.location = ptr::null_mut();
94
+ }
95
+ }
96
+ // boxed_slice drops here, releasing the buffer
97
+ }
98
+ // array drops here
99
+ }
@@ -0,0 +1,85 @@
1
+ //! This file provides the C API for Document accessors
2
+
3
+ use libc::c_char;
4
+ use std::ffi::CString;
5
+ use std::ptr;
6
+
7
+ use crate::definition_api::{DefinitionsIter, rdx_definitions_iter_new_from_ids};
8
+ use crate::graph_api::{GraphPointer, with_graph};
9
+ use rubydex::model::ids::UriId;
10
+
11
+ #[derive(Debug)]
12
+ pub struct DocumentsIter {
13
+ entries: Box<[u64]>,
14
+ index: usize,
15
+ }
16
+
17
+ iterator!(DocumentsIter, entries: u64);
18
+
19
+ /// # Safety
20
+ /// `iter` must be a valid pointer previously returned by `DocumentsIter::new`.
21
+ #[unsafe(no_mangle)]
22
+ pub unsafe extern "C" fn rdx_graph_documents_iter_len(iter: *const DocumentsIter) -> usize {
23
+ unsafe { DocumentsIter::len(iter) }
24
+ }
25
+
26
+ /// # Safety
27
+ /// - `iter` must be a valid pointer previously returned by `DocumentsIter::new`.
28
+ /// - `out` must be a valid, writable pointer.
29
+ #[unsafe(no_mangle)]
30
+ pub unsafe extern "C" fn rdx_graph_documents_iter_next(iter: *mut DocumentsIter, out: *mut u64) -> bool {
31
+ unsafe { DocumentsIter::next(iter, out) }
32
+ }
33
+
34
+ /// # Safety
35
+ /// - `iter` must be a pointer previously returned by `DocumentsIter::new`.
36
+ /// - `iter` must not be used after being freed.
37
+ #[unsafe(no_mangle)]
38
+ pub unsafe extern "C" fn rdx_graph_documents_iter_free(iter: *mut DocumentsIter) {
39
+ unsafe { DocumentsIter::free(iter) }
40
+ }
41
+
42
+ /// Returns the UTF-8 URI string for a document id.
43
+ /// Caller must free with `free_c_string`.
44
+ ///
45
+ /// # Safety
46
+ ///
47
+ /// Assumes pointer is valid.
48
+ ///
49
+ /// # Panics
50
+ ///
51
+ /// This function will panic if the URI pointer is invalid.
52
+ #[unsafe(no_mangle)]
53
+ pub unsafe extern "C" fn rdx_document_uri(pointer: GraphPointer, uri_id: u64) -> *const c_char {
54
+ with_graph(pointer, |graph| {
55
+ let uri_id = UriId::new(uri_id);
56
+ if let Some(doc) = graph.documents().get(&uri_id) {
57
+ CString::new(doc.uri()).unwrap().into_raw().cast_const()
58
+ } else {
59
+ ptr::null()
60
+ }
61
+ })
62
+ }
63
+
64
+ /// An iterator over definition IDs and kinds for a given document (URI)
65
+ ///
66
+ /// We snapshot the IDs at iterator creation so if the graph is modified, the iterator will not see the changes
67
+ // Use shared DefinitionsIter directly in signatures
68
+ /// Creates a new iterator over definition IDs for a given document by snapshotting the current set of IDs.
69
+ ///
70
+ /// # Safety
71
+ ///
72
+ /// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
73
+ /// - The returned pointer must be freed with `rdx_document_definitions_iter_free`.
74
+ #[unsafe(no_mangle)]
75
+ pub unsafe extern "C" fn rdx_document_definitions_iter_new(pointer: GraphPointer, uri_id: u64) -> *mut DefinitionsIter {
76
+ // Snapshot the IDs and kinds at iterator creation to avoid borrowing across FFI calls
77
+ with_graph(pointer, |graph| {
78
+ let uri_id = UriId::new(uri_id);
79
+ if let Some(doc) = graph.documents().get(&uri_id) {
80
+ rdx_definitions_iter_new_from_ids(graph, doc.definitions())
81
+ } else {
82
+ DefinitionsIter::new(Vec::<_>::new().into_boxed_slice())
83
+ }
84
+ })
85
+ }