rubydex 0.1.0.beta1-x86_64-linux → 0.1.0.beta2-x86_64-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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/ext/rubydex/declaration.c +146 -0
  3. data/ext/rubydex/declaration.h +10 -0
  4. data/ext/rubydex/definition.c +234 -0
  5. data/ext/rubydex/definition.h +28 -0
  6. data/ext/rubydex/diagnostic.c +6 -0
  7. data/ext/rubydex/diagnostic.h +11 -0
  8. data/ext/rubydex/document.c +98 -0
  9. data/ext/rubydex/document.h +10 -0
  10. data/ext/rubydex/extconf.rb +36 -15
  11. data/ext/rubydex/graph.c +405 -0
  12. data/ext/rubydex/graph.h +10 -0
  13. data/ext/rubydex/handle.h +44 -0
  14. data/ext/rubydex/location.c +22 -0
  15. data/ext/rubydex/location.h +15 -0
  16. data/ext/rubydex/reference.c +104 -0
  17. data/ext/rubydex/reference.h +16 -0
  18. data/ext/rubydex/rubydex.c +22 -0
  19. data/ext/rubydex/utils.c +27 -0
  20. data/ext/rubydex/utils.h +13 -0
  21. data/lib/rubydex/3.2/rubydex.so +0 -0
  22. data/lib/rubydex/3.3/rubydex.so +0 -0
  23. data/lib/rubydex/3.4/rubydex.so +0 -0
  24. data/lib/rubydex/4.0/rubydex.so +0 -0
  25. data/lib/rubydex/librubydex_sys.so +0 -0
  26. data/lib/rubydex/version.rb +1 -1
  27. data/rust/Cargo.lock +1275 -0
  28. data/rust/Cargo.toml +23 -0
  29. data/rust/about.hbs +78 -0
  30. data/rust/about.toml +9 -0
  31. data/rust/rubydex/Cargo.toml +41 -0
  32. data/rust/rubydex/src/diagnostic.rs +108 -0
  33. data/rust/rubydex/src/errors.rs +28 -0
  34. data/rust/rubydex/src/indexing/local_graph.rs +172 -0
  35. data/rust/rubydex/src/indexing/ruby_indexer.rs +5397 -0
  36. data/rust/rubydex/src/indexing.rs +128 -0
  37. data/rust/rubydex/src/job_queue.rs +186 -0
  38. data/rust/rubydex/src/lib.rs +15 -0
  39. data/rust/rubydex/src/listing.rs +249 -0
  40. data/rust/rubydex/src/main.rs +116 -0
  41. data/rust/rubydex/src/model/comment.rs +24 -0
  42. data/rust/rubydex/src/model/declaration.rs +541 -0
  43. data/rust/rubydex/src/model/definitions.rs +1475 -0
  44. data/rust/rubydex/src/model/document.rs +111 -0
  45. data/rust/rubydex/src/model/encoding.rs +22 -0
  46. data/rust/rubydex/src/model/graph.rs +1387 -0
  47. data/rust/rubydex/src/model/id.rs +90 -0
  48. data/rust/rubydex/src/model/identity_maps.rs +54 -0
  49. data/rust/rubydex/src/model/ids.rs +32 -0
  50. data/rust/rubydex/src/model/name.rs +188 -0
  51. data/rust/rubydex/src/model/references.rs +129 -0
  52. data/rust/rubydex/src/model/string_ref.rs +44 -0
  53. data/rust/rubydex/src/model/visibility.rs +41 -0
  54. data/rust/rubydex/src/model.rs +13 -0
  55. data/rust/rubydex/src/offset.rs +70 -0
  56. data/rust/rubydex/src/position.rs +6 -0
  57. data/rust/rubydex/src/query.rs +103 -0
  58. data/rust/rubydex/src/resolution.rs +4421 -0
  59. data/rust/rubydex/src/stats/memory.rs +71 -0
  60. data/rust/rubydex/src/stats/timer.rs +126 -0
  61. data/rust/rubydex/src/stats.rs +9 -0
  62. data/rust/rubydex/src/test_utils/context.rs +226 -0
  63. data/rust/rubydex/src/test_utils/graph_test.rs +229 -0
  64. data/rust/rubydex/src/test_utils/local_graph_test.rs +166 -0
  65. data/rust/rubydex/src/test_utils.rs +52 -0
  66. data/rust/rubydex/src/visualization/dot.rs +176 -0
  67. data/rust/rubydex/src/visualization.rs +6 -0
  68. data/rust/rubydex/tests/cli.rs +167 -0
  69. data/rust/rubydex-sys/Cargo.toml +20 -0
  70. data/rust/rubydex-sys/build.rs +14 -0
  71. data/rust/rubydex-sys/cbindgen.toml +12 -0
  72. data/rust/rubydex-sys/src/declaration_api.rs +114 -0
  73. data/rust/rubydex-sys/src/definition_api.rs +350 -0
  74. data/rust/rubydex-sys/src/diagnostic_api.rs +99 -0
  75. data/rust/rubydex-sys/src/document_api.rs +54 -0
  76. data/rust/rubydex-sys/src/graph_api.rs +493 -0
  77. data/rust/rubydex-sys/src/lib.rs +9 -0
  78. data/rust/rubydex-sys/src/location_api.rs +79 -0
  79. data/rust/rubydex-sys/src/name_api.rs +81 -0
  80. data/rust/rubydex-sys/src/reference_api.rs +191 -0
  81. data/rust/rubydex-sys/src/utils.rs +50 -0
  82. data/rust/rustfmt.toml +2 -0
  83. metadata +77 -2
@@ -0,0 +1,114 @@
1
+ //! This file provides the C API for the Graph object
2
+
3
+ use libc::c_char;
4
+ use rubydex::model::declaration::Declaration;
5
+ use std::ffi::CString;
6
+ use std::ptr;
7
+
8
+ use crate::definition_api::{DefinitionKind, DefinitionsIter, rdx_definitions_iter_new_from_ids};
9
+ use crate::graph_api::{GraphPointer, with_graph};
10
+ use crate::utils;
11
+ use rubydex::model::ids::{DeclarationId, StringId};
12
+
13
+ /// Returns the UTF-8 name string for a declaration id.
14
+ /// Caller must free with `free_c_string`.
15
+ ///
16
+ /// # Safety
17
+ ///
18
+ /// Assumes pointer is valid.
19
+ ///
20
+ /// # Panics
21
+ ///
22
+ /// This function will panic if the name pointer is invalid.
23
+ #[unsafe(no_mangle)]
24
+ pub unsafe extern "C" fn rdx_declaration_name(pointer: GraphPointer, name_id: i64) -> *const c_char {
25
+ with_graph(pointer, |graph| {
26
+ let name_id = DeclarationId::new(name_id);
27
+ if let Some(decl) = graph.declarations().get(&name_id) {
28
+ CString::new(decl.name()).unwrap().into_raw().cast_const()
29
+ } else {
30
+ ptr::null()
31
+ }
32
+ })
33
+ }
34
+
35
+ /// Returns the declaration ID for a member from a declaration.
36
+ /// Returns NULL if the member is not found.
37
+ ///
38
+ /// # Safety
39
+ /// - `member` must be a valid, null-terminated UTF-8 string
40
+ #[unsafe(no_mangle)]
41
+ pub unsafe extern "C" fn rdx_declaration_member(
42
+ pointer: GraphPointer,
43
+ name_id: i64,
44
+ member: *const c_char,
45
+ ) -> *const i64 {
46
+ let Ok(member_str) = (unsafe { utils::convert_char_ptr_to_string(member) }) else {
47
+ return ptr::null();
48
+ };
49
+
50
+ with_graph(pointer, |graph| {
51
+ let name_id = DeclarationId::new(name_id);
52
+ if let Some(Declaration::Namespace(decl)) = graph.declarations().get(&name_id) {
53
+ let member_id = StringId::from(member_str.as_str());
54
+
55
+ if let Some(member_decl_id) = decl.member(&member_id) {
56
+ return Box::into_raw(Box::new(**member_decl_id)).cast_const();
57
+ }
58
+ }
59
+
60
+ ptr::null()
61
+ })
62
+ }
63
+
64
+ /// Returns the UTF-8 unqualified name string for a declaration id.
65
+ /// Caller must free with `free_c_string`.
66
+ ///
67
+ /// # Safety
68
+ ///
69
+ /// Assumes pointer is valid.
70
+ ///
71
+ /// # Panics
72
+ ///
73
+ /// This function will panic if the name pointer is invalid.
74
+ #[unsafe(no_mangle)]
75
+ pub unsafe extern "C" fn rdx_declaration_unqualified_name(pointer: GraphPointer, name_id: i64) -> *const c_char {
76
+ with_graph(pointer, |graph| {
77
+ let name_id = DeclarationId::new(name_id);
78
+ if let Some(decl) = graph.declarations().get(&name_id) {
79
+ CString::new(decl.unqualified_name()).unwrap().into_raw().cast_const()
80
+ } else {
81
+ ptr::null()
82
+ }
83
+ })
84
+ }
85
+
86
+ /// An iterator over definition IDs and kinds for a given declaration
87
+ ///
88
+ /// We snapshot the IDs at iterator creation so if the graph is modified, the iterator will not see the changes
89
+ // Use shared DefinitionsIter directly in signatures
90
+ /// Creates a new iterator over definition IDs for a given declaration by snapshotting the current set of IDs.
91
+ ///
92
+ /// # Safety
93
+ ///
94
+ /// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
95
+ /// - The returned pointer must be freed with `rdx_declaration_definitions_iter_free`.
96
+ ///
97
+ /// # Panics
98
+ ///
99
+ /// This function will panic if acquiring a read lock fails
100
+ #[unsafe(no_mangle)]
101
+ pub unsafe extern "C" fn rdx_declaration_definitions_iter_new(
102
+ pointer: GraphPointer,
103
+ decl_id: i64,
104
+ ) -> *mut DefinitionsIter {
105
+ // Snapshot the IDs and kinds at iterator creation to avoid borrowing across FFI calls
106
+ with_graph(pointer, |graph| {
107
+ let decl_id = DeclarationId::new(decl_id);
108
+ if let Some(decl) = graph.declarations().get(&decl_id) {
109
+ rdx_definitions_iter_new_from_ids(graph, decl.definitions())
110
+ } else {
111
+ DefinitionsIter::new(Vec::<(i64, DefinitionKind)>::new().into_boxed_slice())
112
+ }
113
+ })
114
+ }
@@ -0,0 +1,350 @@
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 libc::c_char;
6
+ use rubydex::model::definitions::Definition;
7
+ use rubydex::model::ids::DefinitionId;
8
+ use std::ffi::CString;
9
+ use std::ptr;
10
+
11
+ /// C-compatible enum representing the kind of a definition.
12
+ #[repr(C)]
13
+ #[derive(Copy, Clone, Debug, PartialEq, Eq)]
14
+ pub enum DefinitionKind {
15
+ Class = 0,
16
+ SingletonClass = 1,
17
+ Module = 2,
18
+ Constant = 3,
19
+ ConstantAlias = 4,
20
+ Method = 5,
21
+ AttrAccessor = 6,
22
+ AttrReader = 7,
23
+ AttrWriter = 8,
24
+ GlobalVariable = 9,
25
+ InstanceVariable = 10,
26
+ ClassVariable = 11,
27
+ MethodAlias = 12,
28
+ GlobalVariableAlias = 13,
29
+ }
30
+
31
+ pub(crate) fn map_definition_to_kind(defn: &Definition) -> DefinitionKind {
32
+ match defn {
33
+ Definition::Class(_) => DefinitionKind::Class,
34
+ Definition::SingletonClass(_) => DefinitionKind::SingletonClass,
35
+ Definition::Module(_) => DefinitionKind::Module,
36
+ Definition::Constant(_) => DefinitionKind::Constant,
37
+ Definition::ConstantAlias(_) => DefinitionKind::ConstantAlias,
38
+ Definition::Method(_) => DefinitionKind::Method,
39
+ Definition::AttrAccessor(_) => DefinitionKind::AttrAccessor,
40
+ Definition::AttrReader(_) => DefinitionKind::AttrReader,
41
+ Definition::AttrWriter(_) => DefinitionKind::AttrWriter,
42
+ Definition::GlobalVariable(_) => DefinitionKind::GlobalVariable,
43
+ Definition::InstanceVariable(_) => DefinitionKind::InstanceVariable,
44
+ Definition::ClassVariable(_) => DefinitionKind::ClassVariable,
45
+ Definition::MethodAlias(_) => DefinitionKind::MethodAlias,
46
+ Definition::GlobalVariableAlias(_) => DefinitionKind::GlobalVariableAlias,
47
+ }
48
+ }
49
+
50
+ /// Returns the enum kind for a definition id (e.g. Class, Module).
51
+ ///
52
+ /// # Safety
53
+ ///
54
+ /// Assumes pointer is valid.
55
+ ///
56
+ /// # Panics
57
+ ///
58
+ /// This function will panic if the definition cannot be found.
59
+ #[unsafe(no_mangle)]
60
+ pub unsafe extern "C" fn rdx_definition_kind(pointer: GraphPointer, definition_id: i64) -> DefinitionKind {
61
+ with_graph(pointer, |graph| {
62
+ let definition_id = DefinitionId::new(definition_id);
63
+ if let Some(defn) = graph.definitions().get(&definition_id) {
64
+ map_definition_to_kind(defn)
65
+ } else {
66
+ panic!("Definition not found: {definition_id:?}");
67
+ }
68
+ })
69
+ }
70
+
71
+ /// Returns the UTF-8 unqualified name string for a definition id.
72
+ /// Caller must free with `free_c_string`.
73
+ ///
74
+ /// # Safety
75
+ ///
76
+ /// Assumes pointer is valid.
77
+ ///
78
+ /// # Panics
79
+ ///
80
+ /// This function will panic if the definition cannot be found.
81
+ #[unsafe(no_mangle)]
82
+ pub unsafe extern "C" fn rdx_definition_name(pointer: GraphPointer, definition_id: i64) -> *const c_char {
83
+ with_graph(pointer, |graph| {
84
+ let def_id = DefinitionId::new(definition_id);
85
+ if let Some(defn) = graph.definitions().get(&def_id) {
86
+ let string_id = graph.definition_string_id(defn);
87
+
88
+ if let Some(name) = graph.strings().get(&string_id) {
89
+ CString::new(name.as_str()).unwrap().into_raw().cast_const()
90
+ } else {
91
+ ptr::null()
92
+ }
93
+ } else {
94
+ ptr::null()
95
+ }
96
+ })
97
+ }
98
+
99
+ /// Shared iterator over definition (id, kind) pairs
100
+ #[derive(Debug)]
101
+ pub struct DefinitionsIter {
102
+ pub entries: Box<[(i64, DefinitionKind)]>,
103
+ pub index: usize,
104
+ }
105
+
106
+ impl DefinitionsIter {
107
+ #[must_use]
108
+ pub fn new(entries: Box<[(i64, DefinitionKind)]>) -> *mut DefinitionsIter {
109
+ Box::into_raw(Box::new(DefinitionsIter { entries, index: 0 }))
110
+ }
111
+ }
112
+
113
+ /// Returns the total number of entries in the iterator snapshot.
114
+ ///
115
+ /// # Safety
116
+ /// - `iter` must be a valid pointer previously returned by `DefinitionsIter::new`.
117
+ #[unsafe(no_mangle)]
118
+ pub unsafe extern "C" fn rdx_definitions_iter_len(iter: *const DefinitionsIter) -> usize {
119
+ if iter.is_null() {
120
+ return 0;
121
+ }
122
+ unsafe { (&*iter).entries.len() }
123
+ }
124
+
125
+ /// Advances the iterator and writes the next (ID, Kind) into `out_id` and `out_kind`.
126
+ /// Returns `true` if a pair was written, or `false` if the iterator is exhausted or inputs are invalid.
127
+ ///
128
+ /// # Safety
129
+ /// - `iter` must be a valid pointer previously returned by `DefinitionsIter::new`.
130
+ /// - `out_id` and `out_kind` must be valid, writable pointers.
131
+ #[unsafe(no_mangle)]
132
+ pub unsafe extern "C" fn rdx_definitions_iter_next(
133
+ iter: *mut DefinitionsIter,
134
+ out_id: *mut i64,
135
+ out_kind: *mut DefinitionKind,
136
+ ) -> bool {
137
+ if iter.is_null() || out_id.is_null() || out_kind.is_null() {
138
+ return false;
139
+ }
140
+
141
+ let it = unsafe { &mut *iter };
142
+ if it.index >= it.entries.len() {
143
+ return false;
144
+ }
145
+
146
+ let (id, kind) = it.entries[it.index];
147
+ it.index += 1;
148
+ unsafe {
149
+ *out_id = id;
150
+ *out_kind = kind;
151
+ }
152
+ true
153
+ }
154
+
155
+ /// Frees an iterator created by `DefinitionsIter::new`.
156
+ ///
157
+ /// # Safety
158
+ /// - `iter` must be a pointer previously returned by `DefinitionsIter::new`.
159
+ /// - `iter` must not be used after being freed.
160
+ #[unsafe(no_mangle)]
161
+ pub unsafe extern "C" fn rdx_definitions_iter_free(iter: *mut DefinitionsIter) {
162
+ if iter.is_null() {
163
+ return;
164
+ }
165
+ unsafe {
166
+ let _ = Box::from_raw(iter);
167
+ }
168
+ }
169
+
170
+ /// C-compatible struct representing a single comment with its string and location
171
+ #[repr(C)]
172
+ pub struct CommentEntry {
173
+ pub string: *const c_char,
174
+ pub location: *mut Location,
175
+ }
176
+
177
+ /// C-compatible array of comments
178
+ #[repr(C)]
179
+ pub struct CommentArray {
180
+ pub items: *mut CommentEntry,
181
+ pub len: usize,
182
+ }
183
+
184
+ /// Returns a newly allocated array of comments (string and location) for the given definition id.
185
+ /// Caller must free the returned pointer with `rdx_definition_comments_free` and each inner string with `free_c_string` if needed.
186
+ ///
187
+ /// # Safety
188
+ /// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
189
+ /// - `definition_id` must be a valid definition id.
190
+ ///
191
+ /// # Panics
192
+ /// This function will panic if a definition or document cannot be found.
193
+ #[unsafe(no_mangle)]
194
+ pub unsafe extern "C" fn rdx_definition_comments(pointer: GraphPointer, definition_id: i64) -> *mut CommentArray {
195
+ with_graph(pointer, |graph| {
196
+ let def_id = DefinitionId::new(definition_id);
197
+ let Some(defn) = graph.definitions().get(&def_id) else {
198
+ panic!("Definition not found: {definition_id:?}");
199
+ };
200
+
201
+ let uri_id = *defn.uri_id();
202
+ let document = graph.documents().get(&uri_id).expect("document should exist");
203
+
204
+ let mut entries = defn
205
+ .comments()
206
+ .iter()
207
+ .map(|c| CommentEntry {
208
+ string: CString::new(c.string().as_str()).unwrap().into_raw().cast_const(),
209
+ location: create_location_for_uri_and_offset(graph, document, c.offset()),
210
+ })
211
+ .collect::<Vec<CommentEntry>>()
212
+ .into_boxed_slice();
213
+
214
+ let len = entries.len();
215
+ let items_ptr = entries.as_mut_ptr();
216
+ std::mem::forget(entries);
217
+
218
+ Box::into_raw(Box::new(CommentArray { items: items_ptr, len }))
219
+ })
220
+ }
221
+
222
+ /// Frees a `CommentArray` previously returned by `rdx_definition_comments`.
223
+ ///
224
+ /// # Safety
225
+ /// - `ptr` must be a valid pointer previously returned by `rdx_definition_comments`.
226
+ /// - `ptr` must not be used after being freed.
227
+ #[unsafe(no_mangle)]
228
+ pub unsafe extern "C" fn rdx_definition_comments_free(ptr: *mut CommentArray) {
229
+ if ptr.is_null() {
230
+ return;
231
+ }
232
+
233
+ // Take ownership of the CommentArray
234
+ let arr = unsafe { Box::from_raw(ptr) };
235
+
236
+ if !arr.items.is_null() && arr.len > 0 {
237
+ // Reconstruct the boxed slice so we can drop it after freeing inner allocations
238
+ let slice_ptr = ptr::slice_from_raw_parts_mut(arr.items, arr.len);
239
+ let mut boxed_slice: Box<[CommentEntry]> = unsafe { Box::from_raw(slice_ptr) };
240
+
241
+ for item in &mut boxed_slice {
242
+ if !item.string.is_null() {
243
+ // Free the CString allocated for the comment string
244
+ let _ = unsafe { CString::from_raw(item.string.cast_mut()) };
245
+ }
246
+ if !item.location.is_null() {
247
+ unsafe { crate::location_api::rdx_location_free(item.location) };
248
+ item.location = ptr::null_mut();
249
+ }
250
+ }
251
+
252
+ // boxed_slice is dropped here, freeing the items buffer
253
+ }
254
+ // arr is dropped here
255
+ }
256
+
257
+ /// Returns a newly allocated `Location` for the given definition id.
258
+ /// Caller must free the returned pointer with `rdx_location_free`.
259
+ ///
260
+ /// # Safety
261
+ /// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
262
+ /// - `definition_id` must be a valid definition id.
263
+ ///
264
+ /// # Panics
265
+ ///
266
+ /// This function will panic if a definition or document cannot be found.
267
+ #[unsafe(no_mangle)]
268
+ pub unsafe extern "C" fn rdx_definition_location(pointer: GraphPointer, definition_id: i64) -> *mut Location {
269
+ with_graph(pointer, |graph| {
270
+ let def_id = DefinitionId::new(definition_id);
271
+ let Some(defn) = graph.definitions().get(&def_id) else {
272
+ panic!("Definition not found: {definition_id:?}");
273
+ };
274
+
275
+ let document = graph.documents().get(defn.uri_id()).expect("document should exist");
276
+ create_location_for_uri_and_offset(graph, document, defn.offset())
277
+ })
278
+ }
279
+
280
+ /// Creates a new iterator over definition IDs for a given declaration by snapshotting the current set of IDs.
281
+ ///
282
+ /// # Panics
283
+ ///
284
+ /// This function will panic if a definition cannot be found.
285
+ pub(crate) fn rdx_definitions_iter_new_from_ids<'a, I>(
286
+ graph: &rubydex::model::graph::Graph,
287
+ ids: I,
288
+ ) -> *mut DefinitionsIter
289
+ where
290
+ I: IntoIterator<Item = &'a DefinitionId>,
291
+ {
292
+ let entries = ids
293
+ .into_iter()
294
+ .map(|def_id| {
295
+ let id = **def_id;
296
+ let kind = graph
297
+ .definitions()
298
+ .get(&DefinitionId::new(id))
299
+ .map_or_else(|| panic!("Definition not found: {id:?}"), map_definition_to_kind);
300
+ (id, kind)
301
+ })
302
+ .collect::<Vec<(i64, DefinitionKind)>>()
303
+ .into_boxed_slice();
304
+
305
+ DefinitionsIter::new(entries)
306
+ }
307
+
308
+ /// Returns true if the definition is deprecated.
309
+ ///
310
+ /// # Safety
311
+ /// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
312
+ /// - `definition_id` must be a valid definition id.
313
+ ///
314
+ /// # Panics
315
+ /// This function will panic if a definition cannot be found.
316
+ #[unsafe(no_mangle)]
317
+ pub unsafe extern "C" fn rdx_definition_is_deprecated(pointer: GraphPointer, definition_id: i64) -> bool {
318
+ with_graph(pointer, |graph| {
319
+ let def_id = DefinitionId::new(definition_id);
320
+ let defn = graph.definitions().get(&def_id).expect("definition not found");
321
+ defn.is_deprecated()
322
+ })
323
+ }
324
+
325
+ /// Returns a newly allocated `Location` for the name portion of a definition id.
326
+ /// For class, module, and singleton class definitions, this returns the location of just
327
+ /// the name (e.g., "Bar" in `class Foo::Bar`).
328
+ /// For other definition types, returns NULL.
329
+ /// Caller must free the returned pointer with `rdx_location_free`.
330
+ ///
331
+ /// # Safety
332
+ /// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
333
+ /// - `definition_id` must be a valid definition id.
334
+ ///
335
+ /// # Panics
336
+ /// Panics if the definition's document does not exist in the graph.
337
+ #[unsafe(no_mangle)]
338
+ pub unsafe extern "C" fn rdx_definition_name_location(pointer: GraphPointer, definition_id: i64) -> *mut Location {
339
+ with_graph(pointer, |graph| {
340
+ let def_id = DefinitionId::new(definition_id);
341
+ let Some(defn) = graph.definitions().get(&def_id) else {
342
+ return ptr::null_mut();
343
+ };
344
+ let Some(name_offset) = defn.name_offset() else {
345
+ return ptr::null_mut();
346
+ };
347
+ let document = graph.documents().get(defn.uri_id()).expect("document should exist");
348
+ create_location_for_uri_and_offset(graph, document, name_offset)
349
+ })
350
+ }
@@ -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,54 @@
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::{DefinitionKind, DefinitionsIter, rdx_definitions_iter_new_from_ids};
8
+ use crate::graph_api::{GraphPointer, with_graph};
9
+ use rubydex::model::ids::UriId;
10
+
11
+ /// Returns the UTF-8 URI string for a document id.
12
+ /// Caller must free with `free_c_string`.
13
+ ///
14
+ /// # Safety
15
+ ///
16
+ /// Assumes pointer is valid.
17
+ ///
18
+ /// # Panics
19
+ ///
20
+ /// This function will panic if the URI pointer is invalid.
21
+ #[unsafe(no_mangle)]
22
+ pub unsafe extern "C" fn rdx_document_uri(pointer: GraphPointer, uri_id: i64) -> *const c_char {
23
+ with_graph(pointer, |graph| {
24
+ let uri_id = UriId::new(uri_id);
25
+ if let Some(doc) = graph.documents().get(&uri_id) {
26
+ CString::new(doc.uri()).unwrap().into_raw().cast_const()
27
+ } else {
28
+ ptr::null()
29
+ }
30
+ })
31
+ }
32
+
33
+ /// An iterator over definition IDs and kinds for a given document (URI)
34
+ ///
35
+ /// We snapshot the IDs at iterator creation so if the graph is modified, the iterator will not see the changes
36
+ // Use shared DefinitionsIter directly in signatures
37
+ /// Creates a new iterator over definition IDs for a given document by snapshotting the current set of IDs.
38
+ ///
39
+ /// # Safety
40
+ ///
41
+ /// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
42
+ /// - The returned pointer must be freed with `rdx_document_definitions_iter_free`.
43
+ #[unsafe(no_mangle)]
44
+ pub unsafe extern "C" fn rdx_document_definitions_iter_new(pointer: GraphPointer, uri_id: i64) -> *mut DefinitionsIter {
45
+ // Snapshot the IDs and kinds at iterator creation to avoid borrowing across FFI calls
46
+ with_graph(pointer, |graph| {
47
+ let uri_id = UriId::new(uri_id);
48
+ if let Some(doc) = graph.documents().get(&uri_id) {
49
+ rdx_definitions_iter_new_from_ids(graph, doc.definitions())
50
+ } else {
51
+ DefinitionsIter::new(Vec::<(i64, DefinitionKind)>::new().into_boxed_slice())
52
+ }
53
+ })
54
+ }