rubydex 0.3.0 → 0.4.1
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 +29 -7
- data/THIRD_PARTY_LICENSES.html +238 -2
- data/exe/rdx +2 -149
- data/ext/rubydex/config.c +140 -0
- data/ext/rubydex/config.h +16 -0
- data/ext/rubydex/definition.c +34 -0
- data/ext/rubydex/definition.h +3 -0
- data/ext/rubydex/diagnostic.c +28 -1
- data/ext/rubydex/diagnostic.h +2 -0
- data/ext/rubydex/extconf.rb +4 -2
- data/ext/rubydex/graph.c +71 -55
- data/ext/rubydex/graph.h +6 -0
- data/ext/rubydex/query.c +398 -16
- data/ext/rubydex/rubydex.c +2 -0
- data/ext/rubydex/utils.c +11 -4
- 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 +76 -0
- data/lib/rubydex/cli/command/lint.rb +208 -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/rules/dynamic_ancestor.rb +29 -0
- data/lib/rubydex/rules/dynamic_constant_reference.rb +25 -0
- data/lib/rubydex/rules/dynamic_singleton_definition.rb +30 -0
- data/lib/rubydex/rules/invalid_constant_visibility.rb +28 -0
- data/lib/rubydex/rules/invalid_method_visibility.rb +28 -0
- data/lib/rubydex/rules/parse_error.rb +25 -0
- data/lib/rubydex/rules/parse_warning.rb +28 -0
- data/lib/rubydex/rules/top_level_mixin_self.rb +27 -0
- data/lib/rubydex/rules/undefined_constant_visibility_target.rb +27 -0
- data/lib/rubydex/rules/undefined_method_visibility_target.rb +27 -0
- data/lib/rubydex/severity.rb +70 -0
- data/lib/rubydex/skill.rb +89 -0
- data/lib/rubydex/skill_registry.rb +62 -0
- data/lib/rubydex/version.rb +1 -1
- data/lib/rubydex.rb +8 -0
- data/lib/rubydex_linter/rules/rule_structure.rb +140 -0
- data/rbi/rubydex.rbi +464 -21
- data/rust/Cargo.lock +2 -2
- data/rust/rubydex/Cargo.toml +5 -1
- data/rust/rubydex/benches/graph_memory.rs +3 -5
- data/rust/rubydex/src/bin/generate_ruby_rules.rs +94 -0
- data/rust/rubydex/src/config.rs +538 -157
- data/rust/rubydex/src/diagnostic.rs +162 -45
- 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 +270 -6
- data/rust/rubydex/src/indexing/ruby_indexer.rs +10 -12
- data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +134 -81
- data/rust/rubydex/src/lib.rs +1 -0
- data/rust/rubydex/src/listing.rs +26 -1
- data/rust/rubydex/src/main.rs +7 -4
- data/rust/rubydex/src/model/declaration.rs +302 -219
- data/rust/rubydex/src/model/graph.rs +27 -40
- data/rust/rubydex/src/model/name.rs +58 -17
- data/rust/rubydex/src/operation/ruby_builder.rs +30 -45
- data/rust/rubydex/src/path_helpers.rs +77 -0
- data/rust/rubydex/src/query/cypher/schema.rs +63 -0
- data/rust/rubydex/src/query/cypher/tests.rs +26 -1
- data/rust/rubydex/src/query/cypher.rs +8 -11
- data/rust/rubydex/src/query.rs +314 -44
- data/rust/rubydex/src/resolution.rs +139 -187
- data/rust/rubydex/src/resolution_tests.rs +247 -19
- 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/definition_api.rs +68 -1
- data/rust/rubydex-sys/src/diagnostic_api.rs +42 -8
- data/rust/rubydex-sys/src/graph_api.rs +125 -205
- data/rust/rubydex-sys/src/lib.rs +2 -0
- data/rust/rubydex-sys/src/name_api.rs +49 -17
- data/rust/rubydex-sys/src/utils.rs +37 -0
- data/skills/send-private-method/SKILL.md +133 -0
- metadata +42 -2
|
@@ -6,7 +6,9 @@ use crate::location_api::{Location, create_location_for_uri_and_offset};
|
|
|
6
6
|
use crate::reference_api::CConstantReference;
|
|
7
7
|
use libc::c_char;
|
|
8
8
|
use rubydex::model::definitions::{Definition, Mixin};
|
|
9
|
-
use rubydex::model::
|
|
9
|
+
use rubydex::model::graph::Graph;
|
|
10
|
+
use rubydex::model::ids::{DefinitionId, NameId};
|
|
11
|
+
use rubydex::model::name::ParentScope;
|
|
10
12
|
use rubydex::query::AliasResolutionError;
|
|
11
13
|
use std::ffi::CString;
|
|
12
14
|
use std::ptr;
|
|
@@ -110,6 +112,71 @@ pub unsafe extern "C" fn rdx_definition_name(pointer: GraphPointer, definition_i
|
|
|
110
112
|
})
|
|
111
113
|
}
|
|
112
114
|
|
|
115
|
+
fn raw_name_for_name(graph: &Graph, name_id: NameId) -> String {
|
|
116
|
+
let mut raw_name = String::new();
|
|
117
|
+
append_raw_name(graph, name_id, &mut raw_name);
|
|
118
|
+
raw_name
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
fn append_raw_name(graph: &Graph, name_id: NameId, raw_name: &mut String) {
|
|
122
|
+
let name = graph
|
|
123
|
+
.names()
|
|
124
|
+
.get(&name_id)
|
|
125
|
+
.expect("name should exist while building raw_name");
|
|
126
|
+
let simple_name = graph
|
|
127
|
+
.strings()
|
|
128
|
+
.get(name.str())
|
|
129
|
+
.expect("string should exist while building raw_name")
|
|
130
|
+
.as_str();
|
|
131
|
+
|
|
132
|
+
match name.parent_scope() {
|
|
133
|
+
ParentScope::None => {}
|
|
134
|
+
ParentScope::TopLevel => raw_name.push_str("::"),
|
|
135
|
+
ParentScope::Some(parent_id) | ParentScope::Attached(parent_id) => {
|
|
136
|
+
append_raw_name(graph, *parent_id, raw_name);
|
|
137
|
+
raw_name.push_str("::");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
raw_name.push_str(simple_name);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
fn raw_name_id(definition: &Definition) -> Option<&NameId> {
|
|
145
|
+
match definition {
|
|
146
|
+
Definition::Class(_) | Definition::Module(_) | Definition::Constant(_) | Definition::ConstantAlias(_) => {
|
|
147
|
+
definition.name_id()
|
|
148
|
+
}
|
|
149
|
+
_ => None,
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/// Returns the UTF-8 raw name string for a class, module, constant, or constant-alias definition id.
|
|
154
|
+
///
|
|
155
|
+
/// The raw name is reconstructed from the definition's `Name`, preserving explicit parent scopes like `Foo::Bar` and
|
|
156
|
+
/// rooted paths like `::Bar`. Returns null if the definition cannot be found or does not support raw names. Caller must
|
|
157
|
+
/// free a non-null result with `free_c_string`.
|
|
158
|
+
///
|
|
159
|
+
/// # Panics
|
|
160
|
+
///
|
|
161
|
+
/// Panics if the graph contains an invalid name or string reference, or if the name contains an embedded null byte.
|
|
162
|
+
///
|
|
163
|
+
/// # Safety
|
|
164
|
+
///
|
|
165
|
+
/// Assumes pointer is valid.
|
|
166
|
+
///
|
|
167
|
+
#[unsafe(no_mangle)]
|
|
168
|
+
pub unsafe extern "C" fn rdx_definition_raw_name(pointer: GraphPointer, definition_id: u64) -> *const c_char {
|
|
169
|
+
with_graph(pointer, |graph| {
|
|
170
|
+
let def_id = DefinitionId::new(definition_id);
|
|
171
|
+
let Some(name_id) = graph.definitions().get(&def_id).and_then(raw_name_id) else {
|
|
172
|
+
return ptr::null();
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
let name = raw_name_for_name(graph, *name_id);
|
|
176
|
+
CString::new(name).unwrap().into_raw().cast_const()
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
|
|
113
180
|
/// Shared iterator over definition (id, kind) pairs
|
|
114
181
|
#[derive(Debug)]
|
|
115
182
|
pub struct DefinitionsIter {
|
|
@@ -3,12 +3,52 @@
|
|
|
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
|
+
}
|
|
36
|
+
|
|
37
|
+
impl From<Rule> for CRule {
|
|
38
|
+
fn from(rule: Rule) -> Self {
|
|
39
|
+
let name = rule.name();
|
|
40
|
+
|
|
41
|
+
Self {
|
|
42
|
+
name: name.as_ptr().cast::<c_char>(),
|
|
43
|
+
name_length: name.len(),
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
8
48
|
/// C-compatible struct representing a diagnostic entry.
|
|
9
49
|
#[repr(C)]
|
|
10
50
|
pub struct DiagnosticEntry {
|
|
11
|
-
pub rule:
|
|
51
|
+
pub rule: CRule,
|
|
12
52
|
pub message: *const c_char,
|
|
13
53
|
pub location: *mut Location,
|
|
14
54
|
}
|
|
@@ -50,10 +90,7 @@ pub unsafe extern "C" fn rdx_graph_diagnostics(pointer: GraphPointer) -> *mut Di
|
|
|
50
90
|
let location = create_location_for_uri_and_offset(graph, document, diagnostic.offset());
|
|
51
91
|
|
|
52
92
|
DiagnosticEntry {
|
|
53
|
-
rule:
|
|
54
|
-
.unwrap()
|
|
55
|
-
.into_raw()
|
|
56
|
-
.cast_const(),
|
|
93
|
+
rule: CRule::from(*diagnostic.rule()),
|
|
57
94
|
message: CString::new(diagnostic.message()).unwrap().into_raw().cast_const(),
|
|
58
95
|
location,
|
|
59
96
|
}
|
|
@@ -82,9 +119,6 @@ pub unsafe extern "C" fn rdx_diagnostics_free(ptr: *mut DiagnosticArray) {
|
|
|
82
119
|
let mut boxed_slice: Box<[DiagnosticEntry]> = unsafe { Box::from_raw(slice_ptr) };
|
|
83
120
|
|
|
84
121
|
for entry in &mut *boxed_slice {
|
|
85
|
-
if !entry.rule.is_null() {
|
|
86
|
-
let _ = unsafe { CString::from_raw(entry.rule.cast_mut()) };
|
|
87
|
-
}
|
|
88
122
|
if !entry.message.is_null() {
|
|
89
123
|
let _ = unsafe { CString::from_raw(entry.message.cast_mut()) };
|
|
90
124
|
}
|
|
@@ -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,20 +8,20 @@ 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;
|
|
13
|
+
use rubydex::model::definitions::Definition;
|
|
12
14
|
use rubydex::model::encoding::Encoding;
|
|
13
15
|
use rubydex::model::graph::Graph;
|
|
14
|
-
use rubydex::model::ids::{DeclarationId, NameId, UriId, declaration_id_from_lookup_name};
|
|
16
|
+
use rubydex::model::ids::{DeclarationId, DefinitionId, NameId, UriId, declaration_id_from_lookup_name};
|
|
15
17
|
use rubydex::model::keywords;
|
|
16
18
|
use rubydex::model::name::NameRef;
|
|
17
19
|
use rubydex::model::visibility::Visibility;
|
|
18
|
-
use rubydex::query::cypher::{self, OutputFormat};
|
|
19
20
|
use rubydex::query::{CompletionCandidate, CompletionContext, CompletionReceiver};
|
|
20
21
|
use rubydex::resolution::Resolver;
|
|
21
22
|
use rubydex::{indexing, integrity, listing, query};
|
|
22
23
|
use std::ffi::CString;
|
|
23
|
-
use std::path::
|
|
24
|
+
use std::path::PathBuf;
|
|
24
25
|
use std::{mem, ptr, sync::RwLock};
|
|
25
26
|
|
|
26
27
|
pub type GraphPointer = *mut c_void;
|
|
@@ -133,6 +134,27 @@ pub unsafe extern "C" fn rdx_graph_declarations_fuzzy_search(
|
|
|
133
134
|
DeclarationsIter::new(entries)
|
|
134
135
|
}
|
|
135
136
|
|
|
137
|
+
/// Returns an iterator over all dead code candidates in the graph.
|
|
138
|
+
///
|
|
139
|
+
/// # Safety
|
|
140
|
+
///
|
|
141
|
+
/// Expects `pointer` to be a valid graph. The returned iterator must be freed with `rdx_graph_declarations_iter_free`.
|
|
142
|
+
#[unsafe(no_mangle)]
|
|
143
|
+
pub unsafe extern "C" fn rdx_graph_dead_code_candidates(pointer: GraphPointer) -> *mut DeclarationsIter {
|
|
144
|
+
let entries = with_graph(pointer, |graph| {
|
|
145
|
+
query::dead_code_candidates(graph)
|
|
146
|
+
.into_iter()
|
|
147
|
+
.filter_map(|id| {
|
|
148
|
+
let decl = graph.declarations().get(&id)?;
|
|
149
|
+
Some(CDeclaration::from_declaration(id, decl))
|
|
150
|
+
})
|
|
151
|
+
.collect::<Vec<CDeclaration>>()
|
|
152
|
+
.into_boxed_slice()
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
DeclarationsIter::new(entries)
|
|
156
|
+
}
|
|
157
|
+
|
|
136
158
|
/// # Panics
|
|
137
159
|
///
|
|
138
160
|
/// Will panic if the nesting cannot be transformed into a vector of strings
|
|
@@ -155,24 +177,103 @@ pub unsafe extern "C" fn rdx_graph_resolve_constant(
|
|
|
155
177
|
return ptr::null();
|
|
156
178
|
};
|
|
157
179
|
|
|
158
|
-
|
|
180
|
+
resolve_constant_name(graph, name_id, names_to_untrack)
|
|
181
|
+
})
|
|
182
|
+
}
|
|
159
183
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
184
|
+
/// Result of resolving a constant using a namespace definition as its lexical context.
|
|
185
|
+
#[repr(u8)]
|
|
186
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
187
|
+
pub enum CDefinitionConstantResolution {
|
|
188
|
+
Resolved = 0,
|
|
189
|
+
NotFound = 1,
|
|
190
|
+
DefinitionNotFound = 2,
|
|
191
|
+
InvalidDefinitionKind = 3,
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
#[repr(C)]
|
|
195
|
+
#[derive(Debug)]
|
|
196
|
+
pub struct CDefinitionConstantResolutionResult {
|
|
197
|
+
pub status: CDefinitionConstantResolution,
|
|
198
|
+
pub declaration: *const CDeclaration,
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/// Resolves a constant using a namespace definition as its lexical context.
|
|
202
|
+
///
|
|
203
|
+
/// # Panics
|
|
204
|
+
///
|
|
205
|
+
/// Panics if `const_name` cannot be converted to a string.
|
|
206
|
+
///
|
|
207
|
+
/// # Safety
|
|
208
|
+
///
|
|
209
|
+
/// Assumes that `pointer` and `const_name` are valid.
|
|
210
|
+
#[unsafe(no_mangle)]
|
|
211
|
+
pub unsafe extern "C" fn rdx_graph_resolve_constant_from_definition(
|
|
212
|
+
pointer: GraphPointer,
|
|
213
|
+
const_name: *const c_char,
|
|
214
|
+
definition_id: u64,
|
|
215
|
+
) -> CDefinitionConstantResolutionResult {
|
|
216
|
+
with_mut_graph(pointer, |graph| {
|
|
217
|
+
let const_name: String = unsafe { utils::convert_char_ptr_to_string(const_name).unwrap() };
|
|
218
|
+
let nesting = match class_or_module_definition_name_id(graph, DefinitionId::new(definition_id)) {
|
|
219
|
+
Ok(nesting) => nesting,
|
|
220
|
+
Err(status) => {
|
|
221
|
+
return CDefinitionConstantResolutionResult {
|
|
222
|
+
status,
|
|
223
|
+
declaration: ptr::null(),
|
|
224
|
+
};
|
|
164
225
|
}
|
|
165
|
-
None => ptr::null(),
|
|
166
226
|
};
|
|
167
227
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
228
|
+
let Some((name_id, names_to_untrack)) = name_api::name_in_nesting_to_name_id(graph, &const_name, Some(nesting))
|
|
229
|
+
else {
|
|
230
|
+
return CDefinitionConstantResolutionResult {
|
|
231
|
+
status: CDefinitionConstantResolution::NotFound,
|
|
232
|
+
declaration: ptr::null(),
|
|
233
|
+
};
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
let declaration = resolve_constant_name(graph, name_id, names_to_untrack);
|
|
237
|
+
let status = if declaration.is_null() {
|
|
238
|
+
CDefinitionConstantResolution::NotFound
|
|
239
|
+
} else {
|
|
240
|
+
CDefinitionConstantResolution::Resolved
|
|
241
|
+
};
|
|
171
242
|
|
|
172
|
-
declaration
|
|
243
|
+
CDefinitionConstantResolutionResult { status, declaration }
|
|
173
244
|
})
|
|
174
245
|
}
|
|
175
246
|
|
|
247
|
+
fn class_or_module_definition_name_id(
|
|
248
|
+
graph: &Graph,
|
|
249
|
+
definition_id: DefinitionId,
|
|
250
|
+
) -> Result<NameId, CDefinitionConstantResolution> {
|
|
251
|
+
let Some(definition) = graph.definitions().get(&definition_id) else {
|
|
252
|
+
return Err(CDefinitionConstantResolution::DefinitionNotFound);
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
match definition {
|
|
256
|
+
Definition::Class(definition) => Ok(*definition.name_id()),
|
|
257
|
+
Definition::SingletonClass(definition) => Ok(*definition.name_id()),
|
|
258
|
+
Definition::Module(definition) => Ok(*definition.name_id()),
|
|
259
|
+
_ => Err(CDefinitionConstantResolution::InvalidDefinitionKind),
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
fn resolve_constant_name(graph: &mut Graph, name_id: NameId, names_to_untrack: Vec<NameId>) -> *const CDeclaration {
|
|
264
|
+
let declaration_id = Resolver::new(graph).resolve_constant(name_id);
|
|
265
|
+
let declaration = declaration_id.map_or(ptr::null(), |id| {
|
|
266
|
+
let declaration = graph.declarations().get(&id).unwrap();
|
|
267
|
+
Box::into_raw(Box::new(CDeclaration::from_declaration(id, declaration))).cast_const()
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
for name_id in names_to_untrack {
|
|
271
|
+
graph.untrack_name(name_id);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
declaration
|
|
275
|
+
}
|
|
276
|
+
|
|
176
277
|
/// Adds glob patterns to exclude from file discovery during indexing.
|
|
177
278
|
///
|
|
178
279
|
/// # Panics
|
|
@@ -216,14 +317,8 @@ pub unsafe extern "C" fn rdx_graph_excluded_patterns(
|
|
|
216
317
|
|
|
217
318
|
let c_strings: Vec<*const c_char> = excluded
|
|
218
319
|
.iter()
|
|
219
|
-
.filter_map(|
|
|
220
|
-
|
|
221
|
-
// on Windows if a configuration file is using forward slashes. For example:
|
|
222
|
-
//
|
|
223
|
-
// C:\project/vendor/bundle
|
|
224
|
-
let normalized = path.replace(std::path::MAIN_SEPARATOR, "/");
|
|
225
|
-
|
|
226
|
-
CString::new(normalized)
|
|
320
|
+
.filter_map(|pattern| {
|
|
321
|
+
CString::new(pattern.as_ref())
|
|
227
322
|
.ok()
|
|
228
323
|
.map(|c_string| c_string.into_raw().cast_const())
|
|
229
324
|
})
|
|
@@ -236,23 +331,6 @@ pub unsafe extern "C" fn rdx_graph_excluded_patterns(
|
|
|
236
331
|
})
|
|
237
332
|
}
|
|
238
333
|
|
|
239
|
-
/// Sets the workspace path used as the root directory for indexing and relative path resolution. Silently ignores the
|
|
240
|
-
/// call if the given path is not valid UTF-8, leaving the existing workspace path untouched (mirrors
|
|
241
|
-
/// `rdx_graph_set_encoding`). This avoids unwinding across the FFI boundary on malformed input.
|
|
242
|
-
///
|
|
243
|
-
/// # Safety
|
|
244
|
-
///
|
|
245
|
-
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
246
|
-
/// - `path` must be a valid, null-terminated string.
|
|
247
|
-
#[unsafe(no_mangle)]
|
|
248
|
-
pub unsafe extern "C" fn rdx_graph_set_workspace_path(pointer: GraphPointer, path: *const c_char) {
|
|
249
|
-
let Ok(path) = (unsafe { utils::convert_char_ptr_to_string(path) }) else {
|
|
250
|
-
return;
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
with_mut_graph(pointer, |graph| graph.set_workspace_path(PathBuf::from(path)));
|
|
254
|
-
}
|
|
255
|
-
|
|
256
334
|
/// Returns the workspace path as a C string. Caller must free with `free_c_string`.
|
|
257
335
|
///
|
|
258
336
|
/// # Safety
|
|
@@ -261,42 +339,23 @@ pub unsafe extern "C" fn rdx_graph_set_workspace_path(pointer: GraphPointer, pat
|
|
|
261
339
|
#[unsafe(no_mangle)]
|
|
262
340
|
pub unsafe extern "C" fn rdx_graph_workspace_path(pointer: GraphPointer) -> *const c_char {
|
|
263
341
|
with_graph(pointer, |graph| {
|
|
264
|
-
CString::new(graph.workspace_path()
|
|
342
|
+
CString::new(utils::interop_path(graph.workspace_path()))
|
|
265
343
|
.map_or(ptr::null(), |c_string| c_string.into_raw().cast_const())
|
|
266
344
|
})
|
|
267
345
|
}
|
|
268
346
|
|
|
269
|
-
///
|
|
270
|
-
///
|
|
271
|
-
///
|
|
272
|
-
/// `free_c_string`.
|
|
273
|
-
///
|
|
274
|
-
/// A `config_path` that is not valid UTF-8 is reported as an error message.
|
|
347
|
+
/// Applies a parsed configuration file to the graph, which adopts the workspace it was loaded for along with the
|
|
348
|
+
/// settings of its `[graph]` section. This is the only way to point the graph at a workspace other than the current
|
|
349
|
+
/// directory, and it replaces any previously applied configuration. Tool-specific sections are ignored.
|
|
275
350
|
///
|
|
276
351
|
/// # Safety
|
|
277
352
|
///
|
|
278
353
|
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
279
|
-
/// - `
|
|
354
|
+
/// - `config` must be a valid `ConfigPointer` previously returned by `rdx_config_load`.
|
|
280
355
|
#[unsafe(no_mangle)]
|
|
281
|
-
pub unsafe extern "C" fn rdx_graph_load_config(pointer: GraphPointer,
|
|
282
|
-
let
|
|
283
|
-
|
|
284
|
-
graph.load_config(None)
|
|
285
|
-
} else {
|
|
286
|
-
match unsafe { utils::convert_char_ptr_to_string(config_path) } {
|
|
287
|
-
Ok(config_path) => graph.load_config(Some(Path::new(&config_path))),
|
|
288
|
-
Err(_) => Err(Errors::ConfigError("config file path is not valid UTF-8".to_string())),
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
match result {
|
|
294
|
-
Ok(()) => ptr::null(),
|
|
295
|
-
Err(error) => CString::new(error.to_string())
|
|
296
|
-
.unwrap_or_default()
|
|
297
|
-
.into_raw()
|
|
298
|
-
.cast_const(),
|
|
299
|
-
}
|
|
356
|
+
pub unsafe extern "C" fn rdx_graph_load_config(pointer: GraphPointer, config: ConfigPointer) {
|
|
357
|
+
let config = unsafe { &*config.cast::<Config>() };
|
|
358
|
+
with_mut_graph(pointer, |graph| graph.load_config(config));
|
|
300
359
|
}
|
|
301
360
|
|
|
302
361
|
/// Indexes all given file paths in parallel using the provided Graph pointer.
|
|
@@ -1066,145 +1125,6 @@ pub unsafe extern "C" fn rdx_keyword_get(name: *const c_char) -> *const CKeyword
|
|
|
1066
1125
|
}
|
|
1067
1126
|
}
|
|
1068
1127
|
|
|
1069
|
-
/// The result of running a Cypher query, carrying either the formatted output or an error message.
|
|
1070
|
-
#[repr(C)]
|
|
1071
|
-
pub struct CQueryResult {
|
|
1072
|
-
/// Non-null on success; null on error. Caller must free with `free_c_string`.
|
|
1073
|
-
pub output: *const c_char,
|
|
1074
|
-
/// Non-null on error; null on success. Caller must free with `free_c_string`.
|
|
1075
|
-
pub error: *const c_char,
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
impl CQueryResult {
|
|
1079
|
-
fn success(output: &str) -> Self {
|
|
1080
|
-
match CString::new(output) {
|
|
1081
|
-
Ok(c_string) => Self {
|
|
1082
|
-
output: c_string.into_raw().cast_const(),
|
|
1083
|
-
error: ptr::null(),
|
|
1084
|
-
},
|
|
1085
|
-
Err(_) => Self::error("query output contained an interior NUL byte"),
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
fn error(message: &str) -> Self {
|
|
1090
|
-
Self {
|
|
1091
|
-
output: ptr::null(),
|
|
1092
|
-
error: CString::new(message).map_or(ptr::null(), |s| s.into_raw().cast_const()),
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
/// The result of parsing a Cypher query into an opaque, reusable parsed-query object.
|
|
1098
|
-
#[repr(C)]
|
|
1099
|
-
pub struct CParseResult {
|
|
1100
|
-
/// Non-null on success: a heap-allocated parsed query. Free with `rdx_cypher_query_free`.
|
|
1101
|
-
pub query: *mut c_void,
|
|
1102
|
-
/// Non-null on error; null on success. Caller must free with `free_c_string`.
|
|
1103
|
-
pub error: *const c_char,
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
|
-
/// Parses a Cypher query string into an opaque parsed-query object, without needing a graph.
|
|
1107
|
-
///
|
|
1108
|
-
/// On success, `query` is a heap-allocated parsed query that can be executed against a graph with
|
|
1109
|
-
/// `rdx_query_run` and must eventually be freed with `rdx_cypher_query_free`. On failure, `error`
|
|
1110
|
-
/// holds the message.
|
|
1111
|
-
///
|
|
1112
|
-
/// # Safety
|
|
1113
|
-
///
|
|
1114
|
-
/// - `query` must be a valid, null-terminated UTF-8 string.
|
|
1115
|
-
#[unsafe(no_mangle)]
|
|
1116
|
-
pub unsafe extern "C" fn rdx_cypher_parse(query: *const c_char) -> CParseResult {
|
|
1117
|
-
let Ok(query_str) = (unsafe { utils::convert_char_ptr_to_string(query) }) else {
|
|
1118
|
-
return CParseResult {
|
|
1119
|
-
query: ptr::null_mut(),
|
|
1120
|
-
error: CString::new("query is not valid UTF-8").map_or(ptr::null(), |s| s.into_raw().cast_const()),
|
|
1121
|
-
};
|
|
1122
|
-
};
|
|
1123
|
-
|
|
1124
|
-
match cypher::parse(&query_str) {
|
|
1125
|
-
Ok(parsed) => CParseResult {
|
|
1126
|
-
query: Box::into_raw(Box::new(parsed)).cast::<c_void>(),
|
|
1127
|
-
error: ptr::null(),
|
|
1128
|
-
},
|
|
1129
|
-
Err(error) => CParseResult {
|
|
1130
|
-
query: ptr::null_mut(),
|
|
1131
|
-
error: CString::new(error.to_string()).map_or(ptr::null(), |s| s.into_raw().cast_const()),
|
|
1132
|
-
},
|
|
1133
|
-
}
|
|
1134
|
-
}
|
|
1135
|
-
|
|
1136
|
-
/// Frees a parsed query previously returned by `rdx_cypher_parse`.
|
|
1137
|
-
///
|
|
1138
|
-
/// # Safety
|
|
1139
|
-
///
|
|
1140
|
-
/// - `query` must be a pointer returned by `rdx_cypher_parse`, or null. It must not be used after.
|
|
1141
|
-
#[unsafe(no_mangle)]
|
|
1142
|
-
pub unsafe extern "C" fn rdx_cypher_query_free(query: *mut c_void) {
|
|
1143
|
-
if query.is_null() {
|
|
1144
|
-
return;
|
|
1145
|
-
}
|
|
1146
|
-
let _ = unsafe { Box::from_raw(query.cast::<cypher::Query>()) };
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
|
-
/// Executes a previously parsed query (from `rdx_cypher_parse`) against the graph and returns the
|
|
1150
|
-
/// formatted output or an error message. `format` must be `"table"` or `"json"`.
|
|
1151
|
-
///
|
|
1152
|
-
/// # Safety
|
|
1153
|
-
///
|
|
1154
|
-
/// - `query` must be a valid pointer returned by `rdx_cypher_parse`.
|
|
1155
|
-
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
1156
|
-
/// - `format` must be a valid, null-terminated UTF-8 string.
|
|
1157
|
-
#[unsafe(no_mangle)]
|
|
1158
|
-
pub unsafe extern "C" fn rdx_query_run(
|
|
1159
|
-
query: *const c_void,
|
|
1160
|
-
pointer: GraphPointer,
|
|
1161
|
-
format: *const c_char,
|
|
1162
|
-
) -> CQueryResult {
|
|
1163
|
-
if query.is_null() {
|
|
1164
|
-
return CQueryResult::error("query is null");
|
|
1165
|
-
}
|
|
1166
|
-
|
|
1167
|
-
let Ok(format_str) = (unsafe { utils::convert_char_ptr_to_string(format) }) else {
|
|
1168
|
-
return CQueryResult::error("format is not valid UTF-8");
|
|
1169
|
-
};
|
|
1170
|
-
|
|
1171
|
-
let output_format = match format_str.as_str() {
|
|
1172
|
-
"table" => OutputFormat::Table,
|
|
1173
|
-
"json" => OutputFormat::Json,
|
|
1174
|
-
other => {
|
|
1175
|
-
return CQueryResult::error(&format!("unknown query format `{other}` (expected `table` or `json`)"));
|
|
1176
|
-
}
|
|
1177
|
-
};
|
|
1178
|
-
|
|
1179
|
-
let parsed = unsafe { &*query.cast::<cypher::Query>() };
|
|
1180
|
-
|
|
1181
|
-
with_graph(pointer, |graph| {
|
|
1182
|
-
match cypher::run_parsed(graph, parsed, output_format) {
|
|
1183
|
-
Ok(output) => CQueryResult::success(&output),
|
|
1184
|
-
Err(error) => CQueryResult::error(&error.to_string()),
|
|
1185
|
-
}
|
|
1186
|
-
})
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
|
-
/// Returns a description of the queryable Cypher schema (node labels, relationship types, and
|
|
1190
|
-
/// properties) in the given format (`"table"` or `"json"`). The schema is static and requires no
|
|
1191
|
-
/// graph. Caller must free the returned pointer with `free_c_string`.
|
|
1192
|
-
///
|
|
1193
|
-
/// # Safety
|
|
1194
|
-
///
|
|
1195
|
-
/// - `format` must be a valid, null-terminated UTF-8 string.
|
|
1196
|
-
#[unsafe(no_mangle)]
|
|
1197
|
-
pub unsafe extern "C" fn rdx_cypher_schema(format: *const c_char) -> *const c_char {
|
|
1198
|
-
let format_str = unsafe { utils::convert_char_ptr_to_string(format) }.unwrap_or_else(|_| "table".to_string());
|
|
1199
|
-
let output_format = if format_str == "json" {
|
|
1200
|
-
OutputFormat::Json
|
|
1201
|
-
} else {
|
|
1202
|
-
OutputFormat::Table
|
|
1203
|
-
};
|
|
1204
|
-
|
|
1205
|
-
CString::new(cypher::schema(output_format)).map_or(ptr::null(), |s| s.into_raw().cast_const())
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
1128
|
#[repr(u8)]
|
|
1209
1129
|
#[derive(Debug, Clone, Copy)]
|
|
1210
1130
|
pub enum CVisibility {
|