rubydex 0.2.7 → 0.2.8

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.
@@ -19,6 +19,9 @@ fn prints_help() {
19
19
  .success()
20
20
  .stdout(predicate::str::contains("A Static Analysis Toolkit for Ruby"))
21
21
  .stdout(predicate::str::contains("Usage:"))
22
+ .stdout(predicate::str::contains(
23
+ "If the first path is a directory, it is used as the workspace root for rubydex.toml",
24
+ ))
22
25
  .stdout(predicate::str::contains("--stats"))
23
26
  .stdout(predicate::str::contains("--dot"))
24
27
  .stdout(predicate::str::contains("--stop-after"));
@@ -52,6 +55,69 @@ fn paths_argument_variants() {
52
55
  });
53
56
  }
54
57
 
58
+ #[test]
59
+ fn single_directory_argument_is_workspace_root_for_config() {
60
+ with_context(|context| {
61
+ context.write("included.rb", "class Included\nend\n");
62
+ context.write("excluded/skipped.rb", "class Skipped\nend\n");
63
+ context.write("rubydex.toml", "exclude = [\"excluded\"]\n");
64
+
65
+ rdx(&[context.absolute_path().to_str().unwrap()])
66
+ .success()
67
+ .stderr(predicate::str::is_empty())
68
+ .stdout(predicate::str::contains("Indexed 2 files"));
69
+ });
70
+ }
71
+
72
+ #[test]
73
+ fn first_directory_argument_is_workspace_root_for_config() {
74
+ with_context(|context| {
75
+ context.write("included/kept.rb", "class Included\nend\n");
76
+ context.write("excluded/skipped.rb", "class Skipped\nend\n");
77
+ context.write("rubydex.toml", "exclude = [\"excluded\"]\n");
78
+
79
+ rdx(&[
80
+ context.absolute_path().to_str().unwrap(),
81
+ context.absolute_path_to("included").to_str().unwrap(),
82
+ context.absolute_path_to("excluded").to_str().unwrap(),
83
+ ])
84
+ .success()
85
+ .stderr(predicate::str::is_empty())
86
+ .stdout(predicate::str::contains("Indexed 2 files"));
87
+ });
88
+ }
89
+
90
+ #[test]
91
+ fn single_file_argument_is_not_used_as_workspace_root() {
92
+ with_context(|context| {
93
+ context.write("included.rb", "class Included\nend\n");
94
+ context.write("excluded/skipped.rb", "class Skipped\nend\n");
95
+ context.write("rubydex.toml", "exclude = [\"excluded\"]\n");
96
+
97
+ rdx(&[context.absolute_path_to("excluded/skipped.rb").to_str().unwrap()])
98
+ .success()
99
+ .stderr(predicate::str::is_empty())
100
+ .stdout(predicate::str::contains("Indexed 2 files"));
101
+ });
102
+ }
103
+
104
+ #[test]
105
+ fn first_file_argument_is_not_used_as_workspace_root() {
106
+ with_context(|context| {
107
+ context.write("included.rb", "class Included\nend\n");
108
+ context.write("excluded/skipped.rb", "class Skipped\nend\n");
109
+ context.write("rubydex.toml", "exclude = [\"excluded\"]\n");
110
+
111
+ rdx(&[
112
+ context.absolute_path_to("included.rb").to_str().unwrap(),
113
+ context.absolute_path_to("excluded").to_str().unwrap(),
114
+ ])
115
+ .success()
116
+ .stderr(predicate::str::is_empty())
117
+ .stdout(predicate::str::contains("Indexed 3 files"));
118
+ });
119
+ }
120
+
55
121
  #[test]
56
122
  fn prints_index_metrics() {
57
123
  with_context(|context| {
@@ -241,7 +241,7 @@ impl RubydexServer {
241
241
  .to_string();
242
242
  }
243
243
  };
244
- let ids = rubydex::query::declaration_search(graph, &params.query, &match_mode);
244
+ let ids = rubydex::query::declaration_search(graph, &[params.query.as_str()], &match_mode);
245
245
 
246
246
  let limit = params.limit.filter(|&l| l > 0).unwrap_or(50).min(100); // default 50, max 100
247
247
  let offset = params.offset.unwrap_or(0);
@@ -9,7 +9,7 @@ use crate::definition_api::{DefinitionsIter, rdx_definitions_iter_new_from_ids};
9
9
  use crate::graph_api::{GraphPointer, with_graph};
10
10
  use crate::reference_api::{CConstantReference, CMethodReference, ConstantReferencesIter, MethodReferencesIter};
11
11
  use crate::utils;
12
- use rubydex::model::ids::{DeclarationId, StringId};
12
+ use rubydex::model::ids::{DeclarationId, StringId, declaration_id_from_lookup_name};
13
13
 
14
14
  #[repr(C)]
15
15
  #[derive(Debug, Clone, Copy)]
@@ -80,7 +80,7 @@ pub(crate) unsafe fn decl_id_from_char_ptr(ptr: *const c_char) -> Option<Declara
80
80
  if s.is_empty() {
81
81
  return None;
82
82
  }
83
- Some(DeclarationId::from(s.as_str()))
83
+ Some(declaration_id_from_lookup_name(&s))
84
84
  }
85
85
 
86
86
  /// An iterator over declaration IDs
@@ -11,7 +11,7 @@ use rubydex::errors::Errors;
11
11
  use rubydex::indexing::LanguageId;
12
12
  use rubydex::model::encoding::Encoding;
13
13
  use rubydex::model::graph::Graph;
14
- use rubydex::model::ids::{DeclarationId, NameId, UriId};
14
+ use rubydex::model::ids::{DeclarationId, NameId, UriId, declaration_id_from_lookup_name};
15
15
  use rubydex::model::keywords;
16
16
  use rubydex::model::name::NameRef;
17
17
  use rubydex::model::visibility::Visibility;
@@ -58,64 +58,75 @@ where
58
58
  result
59
59
  }
60
60
 
61
- /// Searches the graph using exact substring matching
61
+ /// Searches the graph using exact substring matching, returning every declaration whose name matches any of the
62
+ /// queries.
62
63
  ///
63
64
  /// # Safety
64
65
  ///
65
- /// Expects both the graph and the query pointers to be valid
66
+ /// Expects `pointer` to be a valid graph and `c_queries` to point to an array of `count` valid, NUL-terminated C
67
+ /// strings. Returns a null pointer when `count` is zero, `c_queries` is null, or any query is not valid UTF-8.
66
68
  #[unsafe(no_mangle)]
67
69
  pub unsafe extern "C" fn rdx_graph_declarations_search(
68
70
  pointer: GraphPointer,
69
- c_query: *const c_char,
71
+ c_queries: *const *const c_char,
72
+ count: usize,
70
73
  ) -> *mut DeclarationsIter {
71
- {
72
- let Ok(query) = (unsafe { utils::convert_char_ptr_to_string(c_query) }) else {
73
- return ptr::null_mut();
74
- };
74
+ if count == 0 || c_queries.is_null() {
75
+ return ptr::null_mut();
76
+ }
75
77
 
76
- let entries = with_graph(pointer, |graph| {
77
- query::declaration_search(graph, &query, &query::MatchMode::Exact)
78
- .into_iter()
79
- .filter_map(|id| {
80
- let decl = graph.declarations().get(&id)?;
81
- Some(CDeclaration::from_declaration(id, decl))
82
- })
83
- .collect::<Vec<CDeclaration>>()
84
- .into_boxed_slice()
85
- });
78
+ let Ok(queries) = (unsafe { utils::convert_double_pointer_to_vec(c_queries, count) }) else {
79
+ return ptr::null_mut();
80
+ };
81
+ let query_refs: Vec<&str> = queries.iter().map(String::as_str).collect();
86
82
 
87
- DeclarationsIter::new(entries)
88
- }
83
+ let entries = with_graph(pointer, |graph| {
84
+ query::declaration_search(graph, &query_refs, &query::MatchMode::Exact)
85
+ .into_iter()
86
+ .filter_map(|id| {
87
+ let decl = graph.declarations().get(&id)?;
88
+ Some(CDeclaration::from_declaration(id, decl))
89
+ })
90
+ .collect::<Vec<CDeclaration>>()
91
+ .into_boxed_slice()
92
+ });
93
+
94
+ DeclarationsIter::new(entries)
89
95
  }
90
96
 
91
- /// Searches the graph using fuzzy matching
97
+ /// Searches the graph using fuzzy matching, returning every declaration whose name matches any of the queries.
92
98
  ///
93
99
  /// # Safety
94
100
  ///
95
- /// Expects both the graph and the query pointers to be valid
101
+ /// Expects `pointer` to be a valid graph and `c_queries` to point to an array of `count` valid, NUL-terminated C
102
+ /// strings. Returns a null pointer when `count` is zero, `c_queries` is null, or any query is not valid UTF-8.
96
103
  #[unsafe(no_mangle)]
97
104
  pub unsafe extern "C" fn rdx_graph_declarations_fuzzy_search(
98
105
  pointer: GraphPointer,
99
- c_query: *const c_char,
106
+ c_queries: *const *const c_char,
107
+ count: usize,
100
108
  ) -> *mut DeclarationsIter {
101
- {
102
- let Ok(query) = (unsafe { utils::convert_char_ptr_to_string(c_query) }) else {
103
- return ptr::null_mut();
104
- };
109
+ if count == 0 || c_queries.is_null() {
110
+ return ptr::null_mut();
111
+ }
105
112
 
106
- let entries = with_graph(pointer, |graph| {
107
- query::declaration_search(graph, &query, &query::MatchMode::Fuzzy)
108
- .into_iter()
109
- .filter_map(|id| {
110
- let decl = graph.declarations().get(&id)?;
111
- Some(CDeclaration::from_declaration(id, decl))
112
- })
113
- .collect::<Vec<CDeclaration>>()
114
- .into_boxed_slice()
115
- });
113
+ let Ok(queries) = (unsafe { utils::convert_double_pointer_to_vec(c_queries, count) }) else {
114
+ return ptr::null_mut();
115
+ };
116
+ let query_refs: Vec<&str> = queries.iter().map(String::as_str).collect();
116
117
 
117
- DeclarationsIter::new(entries)
118
- }
118
+ let entries = with_graph(pointer, |graph| {
119
+ query::declaration_search(graph, &query_refs, &query::MatchMode::Fuzzy)
120
+ .into_iter()
121
+ .filter_map(|id| {
122
+ let decl = graph.declarations().get(&id)?;
123
+ Some(CDeclaration::from_declaration(id, decl))
124
+ })
125
+ .collect::<Vec<CDeclaration>>()
126
+ .into_boxed_slice()
127
+ });
128
+
129
+ DeclarationsIter::new(entries)
119
130
  }
120
131
 
121
132
  /// # Panics
@@ -498,7 +509,7 @@ pub unsafe extern "C" fn rdx_graph_get_declaration(pointer: GraphPointer, name:
498
509
  };
499
510
 
500
511
  with_graph(pointer, |graph| {
501
- let decl_id = DeclarationId::from(name_str.as_str());
512
+ let decl_id = declaration_id_from_lookup_name(&name_str);
502
513
 
503
514
  if let Some(decl) = graph.declarations().get(&decl_id) {
504
515
  Box::into_raw(Box::new(CDeclaration::from_declaration(decl_id, decl))).cast_const()
@@ -917,7 +928,7 @@ pub unsafe extern "C" fn rdx_graph_complete_namespace_access(
917
928
  graph,
918
929
  CompletionReceiver::NamespaceAccess {
919
930
  self_decl_id,
920
- namespace_decl_id: DeclarationId::from(name_str.as_str()),
931
+ namespace_decl_id: declaration_id_from_lookup_name(&name_str),
921
932
  },
922
933
  Vec::new(),
923
934
  )
@@ -951,7 +962,7 @@ pub unsafe extern "C" fn rdx_graph_complete_method_call(
951
962
  graph,
952
963
  CompletionReceiver::MethodCall {
953
964
  self_decl_id,
954
- receiver_decl_id: DeclarationId::from(name_str.as_str()),
965
+ receiver_decl_id: declaration_id_from_lookup_name(&name_str),
955
966
  },
956
967
  Vec::new(),
957
968
  )
@@ -995,7 +1006,7 @@ pub unsafe extern "C" fn rdx_graph_complete_method_argument(
995
1006
  CompletionReceiver::MethodArgument {
996
1007
  self_decl_id,
997
1008
  nesting_name_id,
998
- method_decl_id: DeclarationId::from(name_str.as_str()),
1009
+ method_decl_id: declaration_id_from_lookup_name(&name_str),
999
1010
  },
1000
1011
  names_to_untrack,
1001
1012
  )
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubydex
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.7
4
+ version: 0.2.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-06-30 00:00:00.000000000 Z
11
+ date: 2026-07-08 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A high-performance static analysis suite for Ruby, built in Rust with
14
14
  Ruby APIs