rubydex 0.2.9 → 0.4.0
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.
- checksums.yaml +4 -4
- data/README.md +110 -6
- data/THIRD_PARTY_LICENSES.html +271 -2
- data/exe/rdx +2 -73
- data/ext/rubydex/config.c +140 -0
- data/ext/rubydex/config.h +16 -0
- data/ext/rubydex/declaration.c +1 -1
- data/ext/rubydex/definition.c +32 -4
- data/ext/rubydex/diagnostic.c +75 -1
- data/ext/rubydex/diagnostic.h +2 -0
- data/ext/rubydex/graph.c +27 -48
- data/ext/rubydex/graph.h +6 -0
- data/ext/rubydex/query.c +487 -0
- data/ext/rubydex/query.h +8 -0
- data/ext/rubydex/reference.c +60 -0
- data/ext/rubydex/rubydex.c +4 -0
- data/ext/rubydex/utils.c +23 -4
- data/ext/rubydex/utils.h +5 -0
- data/lib/ruby_lsp/rubydex/addon.rb +211 -0
- data/lib/rubydex/cli/command/console.rb +55 -0
- data/lib/rubydex/cli/command/lint/explain.rb +74 -0
- data/lib/rubydex/cli/command/lint.rb +202 -0
- data/lib/rubydex/cli/command/mcp.rb +30 -0
- data/lib/rubydex/cli/command/query.rb +70 -0
- data/lib/rubydex/cli/command/skill.rb +69 -0
- data/lib/rubydex/cli/command.rb +168 -0
- data/lib/rubydex/cli.rb +93 -0
- data/lib/rubydex/config.rb +59 -0
- data/lib/rubydex/diagnostic.rb +12 -3
- data/lib/rubydex/errors.rb +42 -1
- data/lib/rubydex/graph.rb +10 -3
- data/lib/rubydex/linter/custom_rule.rb +97 -0
- data/lib/rubydex/linter/helpers/path_helpers.rb +78 -0
- data/lib/rubydex/linter/helpers/source_access_helpers.rb +31 -0
- data/lib/rubydex/linter/rule_loader.rb +36 -0
- data/lib/rubydex/linter/rule_test_case.rb +343 -0
- data/lib/rubydex/linter/runner.rb +56 -0
- data/lib/rubydex/linter.rb +19 -0
- data/lib/rubydex/location.rb +3 -0
- data/lib/rubydex/mcp_server.rb +1 -2
- data/lib/rubydex/related_information.rb +17 -0
- data/lib/rubydex/rule.rb +33 -0
- data/lib/rubydex/severity.rb +70 -0
- data/lib/rubydex/skill.rb +88 -0
- data/lib/rubydex/skill_registry.rb +62 -0
- data/lib/rubydex/version.rb +1 -1
- data/lib/rubydex.rb +6 -0
- data/lib/rubydex_linter/rules/rule_structure.rb +125 -0
- data/rbi/rubydex.rbi +578 -15
- data/rust/Cargo.lock +7 -0
- data/rust/rubydex/Cargo.toml +1 -0
- data/rust/rubydex/benches/graph_memory.rs +20 -4
- data/rust/rubydex/src/compile_assertions.rs +15 -0
- data/rust/rubydex/src/config.rs +538 -157
- data/rust/rubydex/src/diagnostic.rs +66 -40
- data/rust/rubydex/src/errors.rs +0 -1
- data/rust/rubydex/src/indexing/local_graph.rs +6 -5
- data/rust/rubydex/src/indexing/rbs_indexer.rs +284 -8
- data/rust/rubydex/src/indexing/ruby_indexer.rs +59 -70
- data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +195 -86
- data/rust/rubydex/src/lib.rs +1 -0
- data/rust/rubydex/src/listing.rs +26 -1
- data/rust/rubydex/src/main.rs +9 -128
- data/rust/rubydex/src/model/declaration.rs +301 -229
- data/rust/rubydex/src/model/definitions.rs +27 -26
- data/rust/rubydex/src/model/document.rs +43 -7
- data/rust/rubydex/src/model/graph.rs +67 -68
- data/rust/rubydex/src/model/id.rs +55 -0
- data/rust/rubydex/src/model/ids.rs +21 -9
- data/rust/rubydex/src/model/name.rs +88 -19
- data/rust/rubydex/src/model/references.rs +16 -13
- data/rust/rubydex/src/operation/ruby_builder.rs +78 -104
- data/rust/rubydex/src/path_helpers.rs +77 -0
- data/rust/rubydex/src/query/cypher/schema.rs +853 -0
- data/rust/rubydex/src/query/cypher/schema_info.rs +161 -0
- data/rust/rubydex/src/query/cypher/tests.rs +253 -0
- data/rust/rubydex/src/query/cypher.rs +54 -0
- data/rust/rubydex/src/query.rs +125 -43
- data/rust/rubydex/src/resolution.rs +368 -395
- data/rust/rubydex/src/resolution_tests.rs +504 -78
- data/rust/rubydex/src/test_utils/context.rs +2 -1
- data/rust/rubydex/src/test_utils/graph_test.rs +26 -12
- data/rust/rubydex/src/test_utils/local_graph_test.rs +19 -0
- data/rust/rubydex/tests/cli.rs +4 -4
- data/rust/rubydex-sys/src/config_api.rs +205 -0
- data/rust/rubydex-sys/src/cypher_api.rs +791 -0
- data/rust/rubydex-sys/src/declaration_api.rs +6 -3
- data/rust/rubydex-sys/src/definition_api.rs +27 -7
- data/rust/rubydex-sys/src/diagnostic_api.rs +77 -8
- data/rust/rubydex-sys/src/graph_api.rs +31 -68
- data/rust/rubydex-sys/src/lib.rs +2 -0
- data/rust/rubydex-sys/src/name_api.rs +2 -6
- data/rust/rubydex-sys/src/reference_api.rs +58 -12
- data/rust/rubydex-sys/src/utils.rs +37 -0
- data/skills/send-private-method/SKILL.md +133 -0
- metadata +37 -2
|
@@ -291,7 +291,8 @@ pub unsafe extern "C" fn rdx_declaration_singleton_class(pointer: GraphPointer,
|
|
|
291
291
|
})
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
-
/// Returns the owner of the declaration (attached object in the case of singleton classes)
|
|
294
|
+
/// Returns the owner of the declaration (attached object in the case of singleton classes), or NULL if the declaration
|
|
295
|
+
/// cannot be found.
|
|
295
296
|
///
|
|
296
297
|
/// # Safety
|
|
297
298
|
///
|
|
@@ -299,11 +300,13 @@ pub unsafe extern "C" fn rdx_declaration_singleton_class(pointer: GraphPointer,
|
|
|
299
300
|
///
|
|
300
301
|
/// # Panics
|
|
301
302
|
///
|
|
302
|
-
///
|
|
303
|
+
/// This function will panic if the owner declaration cannot be found.
|
|
303
304
|
#[unsafe(no_mangle)]
|
|
304
305
|
pub unsafe extern "C" fn rdx_declaration_owner(pointer: GraphPointer, decl_id: u64) -> *const CDeclaration {
|
|
305
306
|
with_graph(pointer, |graph| {
|
|
306
|
-
let declaration = graph.declarations().get(&DeclarationId::new(decl_id))
|
|
307
|
+
let Some(declaration) = graph.declarations().get(&DeclarationId::new(decl_id)) else {
|
|
308
|
+
return ptr::null();
|
|
309
|
+
};
|
|
307
310
|
let owner_id = *declaration.owner_id();
|
|
308
311
|
Box::into_raw(Box::new(CDeclaration::from_declaration(
|
|
309
312
|
owner_id,
|
|
@@ -156,21 +156,22 @@ pub struct CommentArray {
|
|
|
156
156
|
pub len: usize,
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
/// Returns a newly allocated array of comments (string and location) for the given definition id
|
|
160
|
-
/// Caller must free the returned pointer
|
|
159
|
+
/// Returns a newly allocated array of comments (string and location) for the given definition id, or NULL if the
|
|
160
|
+
/// definition cannot be found. Caller must free the returned pointer and its contents with
|
|
161
|
+
/// `rdx_definition_comments_free`.
|
|
161
162
|
///
|
|
162
163
|
/// # Safety
|
|
163
164
|
/// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
|
|
164
165
|
/// - `definition_id` must be a valid definition id.
|
|
165
166
|
///
|
|
166
167
|
/// # Panics
|
|
167
|
-
/// This function will panic if
|
|
168
|
+
/// This function will panic if the definition's document cannot be found.
|
|
168
169
|
#[unsafe(no_mangle)]
|
|
169
170
|
pub unsafe extern "C" fn rdx_definition_comments(pointer: GraphPointer, definition_id: u64) -> *mut CommentArray {
|
|
170
171
|
with_graph(pointer, |graph| {
|
|
171
172
|
let def_id = DefinitionId::new(definition_id);
|
|
172
173
|
let Some(defn) = graph.definitions().get(&def_id) else {
|
|
173
|
-
|
|
174
|
+
return ptr::null_mut();
|
|
174
175
|
};
|
|
175
176
|
|
|
176
177
|
let uri_id = *defn.uri_id();
|
|
@@ -229,7 +230,7 @@ pub unsafe extern "C" fn rdx_definition_comments_free(ptr: *mut CommentArray) {
|
|
|
229
230
|
// arr is dropped here
|
|
230
231
|
}
|
|
231
232
|
|
|
232
|
-
/// Returns a newly allocated `Location` for the given definition id.
|
|
233
|
+
/// Returns a newly allocated `Location` for the given definition id, or NULL if the definition cannot be found.
|
|
233
234
|
/// Caller must free the returned pointer with `rdx_location_free`.
|
|
234
235
|
///
|
|
235
236
|
/// # Safety
|
|
@@ -238,13 +239,13 @@ pub unsafe extern "C" fn rdx_definition_comments_free(ptr: *mut CommentArray) {
|
|
|
238
239
|
///
|
|
239
240
|
/// # Panics
|
|
240
241
|
///
|
|
241
|
-
/// This function will panic if
|
|
242
|
+
/// This function will panic if the definition's document cannot be found.
|
|
242
243
|
#[unsafe(no_mangle)]
|
|
243
244
|
pub unsafe extern "C" fn rdx_definition_location(pointer: GraphPointer, definition_id: u64) -> *mut Location {
|
|
244
245
|
with_graph(pointer, |graph| {
|
|
245
246
|
let def_id = DefinitionId::new(definition_id);
|
|
246
247
|
let Some(defn) = graph.definitions().get(&def_id) else {
|
|
247
|
-
|
|
248
|
+
return ptr::null_mut();
|
|
248
249
|
};
|
|
249
250
|
|
|
250
251
|
let document = graph.documents().get(defn.uri_id()).expect("document should exist");
|
|
@@ -493,6 +494,25 @@ pub unsafe extern "C" fn rdx_definition_mixins(pointer: GraphPointer, definition
|
|
|
493
494
|
})
|
|
494
495
|
}
|
|
495
496
|
|
|
497
|
+
/// Returns a pointer to the URI ID of the document a definition belongs to, or
|
|
498
|
+
/// NULL if the definition cannot be found. Caller must free the returned pointer
|
|
499
|
+
/// with `free_u64`.
|
|
500
|
+
///
|
|
501
|
+
/// # Safety
|
|
502
|
+
/// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
|
|
503
|
+
/// - `definition_id` must be a valid definition id.
|
|
504
|
+
#[unsafe(no_mangle)]
|
|
505
|
+
pub unsafe extern "C" fn rdx_definition_document(pointer: GraphPointer, definition_id: u64) -> *const u64 {
|
|
506
|
+
with_graph(pointer, |graph| {
|
|
507
|
+
let def_id = DefinitionId::new(definition_id);
|
|
508
|
+
if let Some(defn) = graph.definitions().get(&def_id) {
|
|
509
|
+
Box::into_raw(Box::new(**defn.uri_id())).cast_const()
|
|
510
|
+
} else {
|
|
511
|
+
ptr::null()
|
|
512
|
+
}
|
|
513
|
+
})
|
|
514
|
+
}
|
|
515
|
+
|
|
496
516
|
/// Status of a `MethodAliasDefinition#target` resolution.
|
|
497
517
|
#[repr(u8)]
|
|
498
518
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
@@ -3,12 +3,87 @@
|
|
|
3
3
|
use crate::graph_api::{GraphPointer, with_graph};
|
|
4
4
|
use crate::location_api::{Location, create_location_for_uri_and_offset};
|
|
5
5
|
use libc::c_char;
|
|
6
|
+
use rubydex::diagnostic::{Rule, Severity};
|
|
6
7
|
use std::{ffi::CString, mem, ptr};
|
|
7
8
|
|
|
9
|
+
/// C-compatible enum representing diagnostic severity levels.
|
|
10
|
+
#[repr(C)]
|
|
11
|
+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
12
|
+
pub enum DiagnosticSeverity {
|
|
13
|
+
Error = 1,
|
|
14
|
+
Warning = 2,
|
|
15
|
+
Information = 3,
|
|
16
|
+
Hint = 4,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
impl From<Severity> for DiagnosticSeverity {
|
|
20
|
+
fn from(severity: Severity) -> Self {
|
|
21
|
+
match severity {
|
|
22
|
+
Severity::Error => DiagnosticSeverity::Error,
|
|
23
|
+
Severity::Warning => DiagnosticSeverity::Warning,
|
|
24
|
+
Severity::Information => DiagnosticSeverity::Information,
|
|
25
|
+
Severity::Hint => DiagnosticSeverity::Hint,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
#[repr(C)]
|
|
31
|
+
#[derive(Debug)]
|
|
32
|
+
pub struct CRule {
|
|
33
|
+
pub name: *const c_char,
|
|
34
|
+
pub name_length: usize,
|
|
35
|
+
pub default_severity: DiagnosticSeverity,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
impl From<Rule> for CRule {
|
|
39
|
+
fn from(rule: Rule) -> Self {
|
|
40
|
+
let name = rule.name();
|
|
41
|
+
|
|
42
|
+
Self {
|
|
43
|
+
name: name.as_ptr().cast::<c_char>(),
|
|
44
|
+
name_length: name.len(),
|
|
45
|
+
default_severity: DiagnosticSeverity::from(rule.default_severity()),
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#[repr(C)]
|
|
51
|
+
pub struct CRuleArray {
|
|
52
|
+
pub items: *mut CRule,
|
|
53
|
+
pub len: usize,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/// Returns every rule the graph can report. Caller must free it with `rdx_rules_free`.
|
|
57
|
+
#[unsafe(no_mangle)]
|
|
58
|
+
pub extern "C" fn rdx_rules() -> CRuleArray {
|
|
59
|
+
let items = Rule::all().iter().copied().map(CRule::from).collect::<Box<[CRule]>>();
|
|
60
|
+
|
|
61
|
+
CRuleArray {
|
|
62
|
+
len: items.len(),
|
|
63
|
+
items: Box::into_raw(items).cast::<CRule>(),
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// Frees an array previously returned by `rdx_rules`.
|
|
68
|
+
///
|
|
69
|
+
/// # Safety
|
|
70
|
+
///
|
|
71
|
+
/// - `rules` must have been returned by `rdx_rules` and must not be used afterwards.
|
|
72
|
+
#[unsafe(no_mangle)]
|
|
73
|
+
pub unsafe extern "C" fn rdx_rules_free(rules: CRuleArray) {
|
|
74
|
+
if rules.items.is_null() {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
unsafe {
|
|
79
|
+
let _ = Box::from_raw(ptr::slice_from_raw_parts_mut(rules.items, rules.len));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
8
83
|
/// C-compatible struct representing a diagnostic entry.
|
|
9
84
|
#[repr(C)]
|
|
10
85
|
pub struct DiagnosticEntry {
|
|
11
|
-
pub rule:
|
|
86
|
+
pub rule: CRule,
|
|
12
87
|
pub message: *const c_char,
|
|
13
88
|
pub location: *mut Location,
|
|
14
89
|
}
|
|
@@ -50,10 +125,7 @@ pub unsafe extern "C" fn rdx_graph_diagnostics(pointer: GraphPointer) -> *mut Di
|
|
|
50
125
|
let location = create_location_for_uri_and_offset(graph, document, diagnostic.offset());
|
|
51
126
|
|
|
52
127
|
DiagnosticEntry {
|
|
53
|
-
rule:
|
|
54
|
-
.unwrap()
|
|
55
|
-
.into_raw()
|
|
56
|
-
.cast_const(),
|
|
128
|
+
rule: CRule::from(*diagnostic.rule()),
|
|
57
129
|
message: CString::new(diagnostic.message()).unwrap().into_raw().cast_const(),
|
|
58
130
|
location,
|
|
59
131
|
}
|
|
@@ -82,9 +154,6 @@ pub unsafe extern "C" fn rdx_diagnostics_free(ptr: *mut DiagnosticArray) {
|
|
|
82
154
|
let mut boxed_slice: Box<[DiagnosticEntry]> = unsafe { Box::from_raw(slice_ptr) };
|
|
83
155
|
|
|
84
156
|
for entry in &mut *boxed_slice {
|
|
85
|
-
if !entry.rule.is_null() {
|
|
86
|
-
let _ = unsafe { CString::from_raw(entry.rule.cast_mut()) };
|
|
87
|
-
}
|
|
88
157
|
if !entry.message.is_null() {
|
|
89
158
|
let _ = unsafe { CString::from_raw(entry.message.cast_mut()) };
|
|
90
159
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
//! This file provides the C API for the Graph object
|
|
2
2
|
|
|
3
|
+
use crate::config_api::ConfigPointer;
|
|
3
4
|
use crate::declaration_api::CDeclaration;
|
|
4
5
|
use crate::declaration_api::DeclarationsIter;
|
|
5
6
|
use crate::declaration_api::decl_id_from_char_ptr;
|
|
@@ -7,7 +8,7 @@ use crate::document_api::DocumentsIter;
|
|
|
7
8
|
use crate::reference_api::{CConstantReference, CMethodReference, ConstantReferencesIter, MethodReferencesIter};
|
|
8
9
|
use crate::{name_api, utils};
|
|
9
10
|
use libc::{c_char, c_void};
|
|
10
|
-
use rubydex::
|
|
11
|
+
use rubydex::config::Config;
|
|
11
12
|
use rubydex::indexing::LanguageId;
|
|
12
13
|
use rubydex::model::encoding::Encoding;
|
|
13
14
|
use rubydex::model::graph::Graph;
|
|
@@ -19,43 +20,46 @@ use rubydex::query::{CompletionCandidate, CompletionContext, CompletionReceiver}
|
|
|
19
20
|
use rubydex::resolution::Resolver;
|
|
20
21
|
use rubydex::{indexing, integrity, listing, query};
|
|
21
22
|
use std::ffi::CString;
|
|
22
|
-
use std::path::
|
|
23
|
-
use std::{mem, ptr};
|
|
23
|
+
use std::path::PathBuf;
|
|
24
|
+
use std::{mem, ptr, sync::RwLock};
|
|
24
25
|
|
|
25
26
|
pub type GraphPointer = *mut c_void;
|
|
26
27
|
|
|
27
|
-
/// Creates a new graph
|
|
28
|
+
/// Creates a new graph wrapped in a `Box<RwLock<Graph>>` for thread-safe shared access across Ractors.
|
|
28
29
|
#[unsafe(no_mangle)]
|
|
29
30
|
pub extern "C" fn rdx_graph_new() -> GraphPointer {
|
|
30
|
-
Box::into_raw(Box::new(Graph::new())) as GraphPointer
|
|
31
|
+
Box::into_raw(Box::new(RwLock::new(Graph::new()))) as GraphPointer
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
/// Frees a Graph through its pointer
|
|
34
35
|
#[unsafe(no_mangle)]
|
|
35
36
|
pub extern "C" fn rdx_graph_free(pointer: GraphPointer) {
|
|
36
37
|
unsafe {
|
|
37
|
-
let _ = Box::from_raw(pointer.cast::<Graph
|
|
38
|
+
let _ = Box::from_raw(pointer.cast::<RwLock<Graph>>());
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
/// Runs `action` against the graph referenced by `pointer` under a read lock.
|
|
43
|
+
///
|
|
44
|
+
/// # Panics
|
|
45
|
+
///
|
|
46
|
+
/// Panics if the `RwLock` is poisoned (a writer panicked while holding the lock).
|
|
41
47
|
pub fn with_graph<F, T>(pointer: GraphPointer, action: F) -> T
|
|
42
48
|
where
|
|
43
49
|
F: FnOnce(&Graph) -> T,
|
|
44
50
|
{
|
|
45
|
-
let
|
|
46
|
-
let
|
|
47
|
-
|
|
48
|
-
result
|
|
51
|
+
let rwlock = unsafe { &*pointer.cast::<RwLock<Graph>>() };
|
|
52
|
+
let guard = rwlock.read().unwrap();
|
|
53
|
+
action(&guard)
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
fn with_mut_graph<F, T>(pointer: GraphPointer, action: F) -> T
|
|
52
57
|
where
|
|
53
58
|
F: FnOnce(&mut Graph) -> T,
|
|
54
59
|
{
|
|
55
|
-
let
|
|
56
|
-
let
|
|
57
|
-
|
|
58
|
-
result
|
|
60
|
+
let rwlock = unsafe { &*pointer.cast::<RwLock<Graph>>() };
|
|
61
|
+
let mut guard = rwlock.write().unwrap();
|
|
62
|
+
action(&mut guard)
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
/// Searches the graph using exact substring matching, returning every declaration whose name matches any of the
|
|
@@ -212,14 +216,8 @@ pub unsafe extern "C" fn rdx_graph_excluded_patterns(
|
|
|
212
216
|
|
|
213
217
|
let c_strings: Vec<*const c_char> = excluded
|
|
214
218
|
.iter()
|
|
215
|
-
.filter_map(|
|
|
216
|
-
|
|
217
|
-
// on Windows if a configuration file is using forward slashes. For example:
|
|
218
|
-
//
|
|
219
|
-
// C:\project/vendor/bundle
|
|
220
|
-
let normalized = path.replace(std::path::MAIN_SEPARATOR, "/");
|
|
221
|
-
|
|
222
|
-
CString::new(normalized)
|
|
219
|
+
.filter_map(|pattern| {
|
|
220
|
+
CString::new(pattern.as_ref())
|
|
223
221
|
.ok()
|
|
224
222
|
.map(|c_string| c_string.into_raw().cast_const())
|
|
225
223
|
})
|
|
@@ -232,23 +230,6 @@ pub unsafe extern "C" fn rdx_graph_excluded_patterns(
|
|
|
232
230
|
})
|
|
233
231
|
}
|
|
234
232
|
|
|
235
|
-
/// Sets the workspace path used as the root directory for indexing and relative path resolution. Silently ignores the
|
|
236
|
-
/// call if the given path is not valid UTF-8, leaving the existing workspace path untouched (mirrors
|
|
237
|
-
/// `rdx_graph_set_encoding`). This avoids unwinding across the FFI boundary on malformed input.
|
|
238
|
-
///
|
|
239
|
-
/// # Safety
|
|
240
|
-
///
|
|
241
|
-
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
242
|
-
/// - `path` must be a valid, null-terminated string.
|
|
243
|
-
#[unsafe(no_mangle)]
|
|
244
|
-
pub unsafe extern "C" fn rdx_graph_set_workspace_path(pointer: GraphPointer, path: *const c_char) {
|
|
245
|
-
let Ok(path) = (unsafe { utils::convert_char_ptr_to_string(path) }) else {
|
|
246
|
-
return;
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
with_mut_graph(pointer, |graph| graph.set_workspace_path(PathBuf::from(path)));
|
|
250
|
-
}
|
|
251
|
-
|
|
252
233
|
/// Returns the workspace path as a C string. Caller must free with `free_c_string`.
|
|
253
234
|
///
|
|
254
235
|
/// # Safety
|
|
@@ -257,42 +238,23 @@ pub unsafe extern "C" fn rdx_graph_set_workspace_path(pointer: GraphPointer, pat
|
|
|
257
238
|
#[unsafe(no_mangle)]
|
|
258
239
|
pub unsafe extern "C" fn rdx_graph_workspace_path(pointer: GraphPointer) -> *const c_char {
|
|
259
240
|
with_graph(pointer, |graph| {
|
|
260
|
-
CString::new(graph.workspace_path()
|
|
241
|
+
CString::new(utils::interop_path(graph.workspace_path()))
|
|
261
242
|
.map_or(ptr::null(), |c_string| c_string.into_raw().cast_const())
|
|
262
243
|
})
|
|
263
244
|
}
|
|
264
245
|
|
|
265
|
-
///
|
|
266
|
-
///
|
|
267
|
-
///
|
|
268
|
-
/// `free_c_string`.
|
|
269
|
-
///
|
|
270
|
-
/// A `config_path` that is not valid UTF-8 is reported as an error message.
|
|
246
|
+
/// Applies a parsed configuration file to the graph, which adopts the workspace it was loaded for along with the
|
|
247
|
+
/// settings of its `[graph]` section. This is the only way to point the graph at a workspace other than the current
|
|
248
|
+
/// directory, and it replaces any previously applied configuration. Tool-specific sections are ignored.
|
|
271
249
|
///
|
|
272
250
|
/// # Safety
|
|
273
251
|
///
|
|
274
252
|
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
275
|
-
/// - `
|
|
253
|
+
/// - `config` must be a valid `ConfigPointer` previously returned by `rdx_config_load`.
|
|
276
254
|
#[unsafe(no_mangle)]
|
|
277
|
-
pub unsafe extern "C" fn rdx_graph_load_config(pointer: GraphPointer,
|
|
278
|
-
let
|
|
279
|
-
|
|
280
|
-
graph.load_config(None)
|
|
281
|
-
} else {
|
|
282
|
-
match unsafe { utils::convert_char_ptr_to_string(config_path) } {
|
|
283
|
-
Ok(config_path) => graph.load_config(Some(Path::new(&config_path))),
|
|
284
|
-
Err(_) => Err(Errors::ConfigError("config file path is not valid UTF-8".to_string())),
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
match result {
|
|
290
|
-
Ok(()) => ptr::null(),
|
|
291
|
-
Err(error) => CString::new(error.to_string())
|
|
292
|
-
.unwrap_or_default()
|
|
293
|
-
.into_raw()
|
|
294
|
-
.cast_const(),
|
|
295
|
-
}
|
|
255
|
+
pub unsafe extern "C" fn rdx_graph_load_config(pointer: GraphPointer, config: ConfigPointer) {
|
|
256
|
+
let config = unsafe { &*config.cast::<Config>() };
|
|
257
|
+
with_mut_graph(pointer, |graph| graph.load_config(config));
|
|
296
258
|
}
|
|
297
259
|
|
|
298
260
|
/// Indexes all given file paths in parallel using the provided Graph pointer.
|
|
@@ -1170,7 +1132,7 @@ mod tests {
|
|
|
1170
1132
|
.ref_count()
|
|
1171
1133
|
);
|
|
1172
1134
|
|
|
1173
|
-
let graph_ptr = Box::into_raw(Box::new(graph)) as GraphPointer;
|
|
1135
|
+
let graph_ptr = Box::into_raw(Box::new(RwLock::new(graph))) as GraphPointer;
|
|
1174
1136
|
|
|
1175
1137
|
// Build the nesting array: ["Foo"] since BAR is inside class Foo
|
|
1176
1138
|
let nesting_strings = [CString::new("Foo").unwrap()];
|
|
@@ -1186,7 +1148,8 @@ mod tests {
|
|
|
1186
1148
|
assert_eq!((*decl).id(), *DeclarationId::from("Foo::BAR"));
|
|
1187
1149
|
};
|
|
1188
1150
|
|
|
1189
|
-
let graph = unsafe { Box::from_raw(graph_ptr.cast::<Graph
|
|
1151
|
+
let graph = unsafe { Box::from_raw(graph_ptr.cast::<RwLock<Graph>>()) };
|
|
1152
|
+
let graph = graph.read().unwrap();
|
|
1190
1153
|
|
|
1191
1154
|
assert_eq!(
|
|
1192
1155
|
1,
|
data/rust/rubydex-sys/src/lib.rs
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
use rubydex::model::{
|
|
2
|
-
graph::Graph,
|
|
3
|
-
ids::NameId,
|
|
4
|
-
name::{Name, ParentScope},
|
|
5
|
-
};
|
|
1
|
+
use rubydex::model::{graph::Graph, ids::NameId, name::ParentScope};
|
|
6
2
|
|
|
7
3
|
/// Takes a constant name and a nesting stack (e.g.: `["Foo", "Bar::Baz", "Qux"]`) and transforms it into a `NameId`,
|
|
8
4
|
/// registering each required part in the graph. Returns the `NameId` and a list of name ids that need to be untracked
|
|
@@ -74,7 +70,7 @@ fn process_qualified_name(
|
|
|
74
70
|
};
|
|
75
71
|
|
|
76
72
|
let str_id = graph.intern_string(part.to_owned());
|
|
77
|
-
let name_id = graph.add_name(
|
|
73
|
+
let name_id = graph.add_name(str_id, parent_scope, nesting_for_part);
|
|
78
74
|
names_to_untrack.push(name_id);
|
|
79
75
|
*current_name = ParentScope::Some(name_id);
|
|
80
76
|
}
|
|
@@ -120,7 +120,7 @@ pub unsafe extern "C" fn rdx_method_references_iter_free(iter: *mut MethodRefere
|
|
|
120
120
|
unsafe { MethodReferencesIter::free(iter) }
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
/// Returns the UTF-8 name string for a constant reference id.
|
|
123
|
+
/// Returns the UTF-8 name string for a constant reference id, or NULL if the reference cannot be found.
|
|
124
124
|
/// Caller must free with `free_c_string`.
|
|
125
125
|
///
|
|
126
126
|
/// # Safety
|
|
@@ -129,12 +129,14 @@ pub unsafe extern "C" fn rdx_method_references_iter_free(iter: *mut MethodRefere
|
|
|
129
129
|
///
|
|
130
130
|
/// # Panics
|
|
131
131
|
///
|
|
132
|
-
/// This function will panic if the reference cannot be found.
|
|
132
|
+
/// This function will panic if the reference's name cannot be found.
|
|
133
133
|
#[unsafe(no_mangle)]
|
|
134
134
|
pub unsafe extern "C" fn rdx_constant_reference_name(pointer: GraphPointer, reference_id: u64) -> *const c_char {
|
|
135
135
|
with_graph(pointer, |graph| {
|
|
136
136
|
let ref_id = ConstantReferenceId::new(reference_id);
|
|
137
|
-
let reference = graph.constant_references().get(&ref_id)
|
|
137
|
+
let Some(reference) = graph.constant_references().get(&ref_id) else {
|
|
138
|
+
return ptr::null();
|
|
139
|
+
};
|
|
138
140
|
let name = graph.names().get(reference.name_id()).expect("Name ID should exist");
|
|
139
141
|
|
|
140
142
|
let name_string = graph
|
|
@@ -146,7 +148,7 @@ pub unsafe extern "C" fn rdx_constant_reference_name(pointer: GraphPointer, refe
|
|
|
146
148
|
})
|
|
147
149
|
}
|
|
148
150
|
|
|
149
|
-
/// Returns the UTF-8 name string for a method reference id.
|
|
151
|
+
/// Returns the UTF-8 name string for a method reference id, or NULL if the reference cannot be found.
|
|
150
152
|
/// Caller must free with `free_c_string`.
|
|
151
153
|
///
|
|
152
154
|
/// # Safety
|
|
@@ -155,12 +157,14 @@ pub unsafe extern "C" fn rdx_constant_reference_name(pointer: GraphPointer, refe
|
|
|
155
157
|
///
|
|
156
158
|
/// # Panics
|
|
157
159
|
///
|
|
158
|
-
/// This function will panic if the reference cannot be found.
|
|
160
|
+
/// This function will panic if the reference's name cannot be found.
|
|
159
161
|
#[unsafe(no_mangle)]
|
|
160
162
|
pub unsafe extern "C" fn rdx_method_reference_name(pointer: GraphPointer, reference_id: u64) -> *const c_char {
|
|
161
163
|
with_graph(pointer, |graph| {
|
|
162
164
|
let ref_id = MethodReferenceId::new(reference_id);
|
|
163
|
-
let reference = graph.method_references().get(&ref_id)
|
|
165
|
+
let Some(reference) = graph.method_references().get(&ref_id) else {
|
|
166
|
+
return ptr::null();
|
|
167
|
+
};
|
|
164
168
|
let name = graph
|
|
165
169
|
.strings()
|
|
166
170
|
.get(reference.str())
|
|
@@ -170,7 +174,7 @@ pub unsafe extern "C" fn rdx_method_reference_name(pointer: GraphPointer, refere
|
|
|
170
174
|
})
|
|
171
175
|
}
|
|
172
176
|
|
|
173
|
-
/// Returns a newly allocated `Location` for the given constant reference id.
|
|
177
|
+
/// Returns a newly allocated `Location` for the given constant reference id, or NULL if the reference cannot be found.
|
|
174
178
|
/// Caller must free the returned pointer with `rdx_location_free`.
|
|
175
179
|
///
|
|
176
180
|
/// # Safety
|
|
@@ -180,12 +184,14 @@ pub unsafe extern "C" fn rdx_method_reference_name(pointer: GraphPointer, refere
|
|
|
180
184
|
///
|
|
181
185
|
/// # Panics
|
|
182
186
|
///
|
|
183
|
-
/// This function will panic if
|
|
187
|
+
/// This function will panic if the reference's document cannot be found.
|
|
184
188
|
#[unsafe(no_mangle)]
|
|
185
189
|
pub unsafe extern "C" fn rdx_constant_reference_location(pointer: GraphPointer, reference_id: u64) -> *mut Location {
|
|
186
190
|
with_graph(pointer, |graph| {
|
|
187
191
|
let ref_id = ConstantReferenceId::new(reference_id);
|
|
188
|
-
let reference = graph.constant_references().get(&ref_id)
|
|
192
|
+
let Some(reference) = graph.constant_references().get(&ref_id) else {
|
|
193
|
+
return ptr::null_mut();
|
|
194
|
+
};
|
|
189
195
|
let document = graph
|
|
190
196
|
.documents()
|
|
191
197
|
.get(&reference.uri_id())
|
|
@@ -226,6 +232,25 @@ pub unsafe extern "C" fn rdx_resolved_constant_reference_declaration(
|
|
|
226
232
|
})
|
|
227
233
|
}
|
|
228
234
|
|
|
235
|
+
/// Returns a pointer to the URI ID of the document a constant reference belongs
|
|
236
|
+
/// to, or NULL if the reference cannot be found. Caller must free the returned
|
|
237
|
+
/// pointer with `free_u64`.
|
|
238
|
+
///
|
|
239
|
+
/// # Safety
|
|
240
|
+
/// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
|
|
241
|
+
/// - `reference_id` must be a valid reference id.
|
|
242
|
+
#[unsafe(no_mangle)]
|
|
243
|
+
pub unsafe extern "C" fn rdx_constant_reference_document(pointer: GraphPointer, reference_id: u64) -> *const u64 {
|
|
244
|
+
with_graph(pointer, |graph| {
|
|
245
|
+
let ref_id = ConstantReferenceId::new(reference_id);
|
|
246
|
+
if let Some(reference) = graph.constant_references().get(&ref_id) {
|
|
247
|
+
Box::into_raw(Box::new(*reference.uri_id())).cast_const()
|
|
248
|
+
} else {
|
|
249
|
+
ptr::null()
|
|
250
|
+
}
|
|
251
|
+
})
|
|
252
|
+
}
|
|
253
|
+
|
|
229
254
|
/// Returns the declaration of the resolved receiver for the given method reference. Returns NULL when the method
|
|
230
255
|
/// reference has no tracked receiver or when the receiver could not be resolved. Caller must free with
|
|
231
256
|
/// `free_c_declaration`.
|
|
@@ -263,7 +288,7 @@ pub unsafe extern "C" fn rdx_method_reference_receiver_declaration(
|
|
|
263
288
|
})
|
|
264
289
|
}
|
|
265
290
|
|
|
266
|
-
/// Returns a newly allocated `Location` for the given method reference id.
|
|
291
|
+
/// Returns a newly allocated `Location` for the given method reference id, or NULL if the reference cannot be found.
|
|
267
292
|
/// Caller must free the returned pointer with `rdx_location_free`.
|
|
268
293
|
///
|
|
269
294
|
/// # Safety
|
|
@@ -273,12 +298,14 @@ pub unsafe extern "C" fn rdx_method_reference_receiver_declaration(
|
|
|
273
298
|
///
|
|
274
299
|
/// # Panics
|
|
275
300
|
///
|
|
276
|
-
/// This function will panic if
|
|
301
|
+
/// This function will panic if the reference's document cannot be found.
|
|
277
302
|
#[unsafe(no_mangle)]
|
|
278
303
|
pub unsafe extern "C" fn rdx_method_reference_location(pointer: GraphPointer, reference_id: u64) -> *mut Location {
|
|
279
304
|
with_graph(pointer, |graph| {
|
|
280
305
|
let ref_id = MethodReferenceId::new(reference_id);
|
|
281
|
-
let reference = graph.method_references().get(&ref_id)
|
|
306
|
+
let Some(reference) = graph.method_references().get(&ref_id) else {
|
|
307
|
+
return ptr::null_mut();
|
|
308
|
+
};
|
|
282
309
|
let document = graph
|
|
283
310
|
.documents()
|
|
284
311
|
.get(&reference.uri_id())
|
|
@@ -288,6 +315,25 @@ pub unsafe extern "C" fn rdx_method_reference_location(pointer: GraphPointer, re
|
|
|
288
315
|
})
|
|
289
316
|
}
|
|
290
317
|
|
|
318
|
+
/// Returns a pointer to the URI ID of the document a method reference belongs
|
|
319
|
+
/// to, or NULL if the reference cannot be found. Caller must free the returned
|
|
320
|
+
/// pointer with `free_u64`.
|
|
321
|
+
///
|
|
322
|
+
/// # Safety
|
|
323
|
+
/// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
|
|
324
|
+
/// - `reference_id` must be a valid reference id.
|
|
325
|
+
#[unsafe(no_mangle)]
|
|
326
|
+
pub unsafe extern "C" fn rdx_method_reference_document(pointer: GraphPointer, reference_id: u64) -> *const u64 {
|
|
327
|
+
with_graph(pointer, |graph| {
|
|
328
|
+
let ref_id = MethodReferenceId::new(reference_id);
|
|
329
|
+
if let Some(reference) = graph.method_references().get(&ref_id) {
|
|
330
|
+
Box::into_raw(Box::new(*reference.uri_id())).cast_const()
|
|
331
|
+
} else {
|
|
332
|
+
ptr::null()
|
|
333
|
+
}
|
|
334
|
+
})
|
|
335
|
+
}
|
|
336
|
+
|
|
291
337
|
/// Frees a `CConstantReference` previously returned by an FFI function.
|
|
292
338
|
///
|
|
293
339
|
/// # Safety
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
use libc::{c_char, size_t};
|
|
2
2
|
use std::ffi::{CStr, CString};
|
|
3
|
+
use std::path::Path;
|
|
3
4
|
use std::slice;
|
|
4
5
|
use std::str::Utf8Error;
|
|
5
6
|
|
|
@@ -68,3 +69,39 @@ pub unsafe extern "C" fn free_c_string_array(ptr: *const *const c_char, count: u
|
|
|
68
69
|
.map(|arg| unsafe { CString::from_raw((*arg).cast_mut()) })
|
|
69
70
|
.collect();
|
|
70
71
|
}
|
|
72
|
+
|
|
73
|
+
/// Converts a Rust `&str` to an owned C string (`*const c_char`), suitable for returning across the
|
|
74
|
+
/// FFI boundary. The caller is responsible for freeing it with `free_c_string`.
|
|
75
|
+
///
|
|
76
|
+
/// # Panics
|
|
77
|
+
///
|
|
78
|
+
/// Panics if `value` contains an interior null byte, which should never occur in rubydex data.
|
|
79
|
+
#[must_use]
|
|
80
|
+
pub fn cstring_raw(value: &str) -> *const c_char {
|
|
81
|
+
CString::new(value).unwrap().into_raw().cast_const()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// Rust uses backslashes as separators on Windows, but Ruby prefers forward slashes everywhere. We need to make sure
|
|
85
|
+
/// we're maintaining the right separators at the boundary.
|
|
86
|
+
#[must_use]
|
|
87
|
+
pub fn interop_path(path: &Path) -> String {
|
|
88
|
+
path.to_string_lossy().replace(std::path::MAIN_SEPARATOR, "/")
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
#[cfg(test)]
|
|
92
|
+
mod tests {
|
|
93
|
+
use super::*;
|
|
94
|
+
|
|
95
|
+
#[cfg(unix)]
|
|
96
|
+
#[test]
|
|
97
|
+
fn interop_path_leaves_a_unix_path_untouched() {
|
|
98
|
+
// A backslash is an ordinary character in a Unix file name, so rewriting it would name a different file.
|
|
99
|
+
assert_eq!(interop_path(Path::new(r"/tmp/we\ird")), r"/tmp/we\ird");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
#[cfg(windows)]
|
|
103
|
+
#[test]
|
|
104
|
+
fn interop_path_separates_a_windows_path_with_forward_slashes() {
|
|
105
|
+
assert_eq!(interop_path(Path::new(r"D:\a\_temp\project")), "D:/a/_temp/project");
|
|
106
|
+
}
|
|
107
|
+
}
|