@skillsmith/core 0.2.0 → 2.0.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.
Files changed (233) hide show
  1. package/README.md +233 -2
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/src/analysis/__tests__/incremental.test.d.ts +13 -0
  4. package/dist/src/analysis/__tests__/incremental.test.d.ts.map +1 -0
  5. package/dist/src/analysis/__tests__/incremental.test.js +515 -0
  6. package/dist/src/analysis/__tests__/incremental.test.js.map +1 -0
  7. package/dist/src/analysis/__tests__/integration.test.d.ts +14 -0
  8. package/dist/src/analysis/__tests__/integration.test.d.ts.map +1 -0
  9. package/dist/src/analysis/__tests__/integration.test.js +1059 -0
  10. package/dist/src/analysis/__tests__/integration.test.js.map +1 -0
  11. package/dist/src/analysis/__tests__/metrics.test.d.ts +9 -0
  12. package/dist/src/analysis/__tests__/metrics.test.d.ts.map +1 -0
  13. package/dist/src/analysis/__tests__/metrics.test.js +369 -0
  14. package/dist/src/analysis/__tests__/metrics.test.js.map +1 -0
  15. package/dist/src/analysis/__tests__/performance.test.d.ts +15 -0
  16. package/dist/src/analysis/__tests__/performance.test.d.ts.map +1 -0
  17. package/dist/src/analysis/__tests__/performance.test.js +402 -0
  18. package/dist/src/analysis/__tests__/performance.test.js.map +1 -0
  19. package/dist/src/analysis/adapters/__tests__/go.test.d.ts +12 -0
  20. package/dist/src/analysis/adapters/__tests__/go.test.d.ts.map +1 -0
  21. package/dist/src/analysis/adapters/__tests__/go.test.js +561 -0
  22. package/dist/src/analysis/adapters/__tests__/go.test.js.map +1 -0
  23. package/dist/src/analysis/adapters/__tests__/python.test.d.ts +11 -0
  24. package/dist/src/analysis/adapters/__tests__/python.test.d.ts.map +1 -0
  25. package/dist/src/analysis/adapters/__tests__/python.test.js +669 -0
  26. package/dist/src/analysis/adapters/__tests__/python.test.js.map +1 -0
  27. package/dist/src/analysis/adapters/__tests__/rust.test.d.ts +12 -0
  28. package/dist/src/analysis/adapters/__tests__/rust.test.d.ts.map +1 -0
  29. package/dist/src/analysis/adapters/__tests__/rust.test.js +676 -0
  30. package/dist/src/analysis/adapters/__tests__/rust.test.js.map +1 -0
  31. package/dist/src/analysis/adapters/__tests__/typescript.test.d.ts +14 -0
  32. package/dist/src/analysis/adapters/__tests__/typescript.test.d.ts.map +1 -0
  33. package/dist/src/analysis/adapters/__tests__/typescript.test.js +381 -0
  34. package/dist/src/analysis/adapters/__tests__/typescript.test.js.map +1 -0
  35. package/dist/src/analysis/adapters/base.d.ts +83 -0
  36. package/dist/src/analysis/adapters/base.d.ts.map +1 -0
  37. package/dist/src/analysis/adapters/base.js +40 -0
  38. package/dist/src/analysis/adapters/base.js.map +1 -0
  39. package/dist/src/analysis/adapters/factory.d.ts +150 -0
  40. package/dist/src/analysis/adapters/factory.d.ts.map +1 -0
  41. package/dist/src/analysis/adapters/factory.js +244 -0
  42. package/dist/src/analysis/adapters/factory.js.map +1 -0
  43. package/dist/src/analysis/adapters/go.d.ts +131 -0
  44. package/dist/src/analysis/adapters/go.d.ts.map +1 -0
  45. package/dist/src/analysis/adapters/go.js +414 -0
  46. package/dist/src/analysis/adapters/go.js.map +1 -0
  47. package/dist/src/analysis/adapters/index.d.ts +20 -0
  48. package/dist/src/analysis/adapters/index.d.ts.map +1 -0
  49. package/dist/src/analysis/adapters/index.js +23 -0
  50. package/dist/src/analysis/adapters/index.js.map +1 -0
  51. package/dist/src/analysis/adapters/java.d.ts +154 -0
  52. package/dist/src/analysis/adapters/java.d.ts.map +1 -0
  53. package/dist/src/analysis/adapters/java.js +407 -0
  54. package/dist/src/analysis/adapters/java.js.map +1 -0
  55. package/dist/src/analysis/adapters/python.d.ts +165 -0
  56. package/dist/src/analysis/adapters/python.d.ts.map +1 -0
  57. package/dist/src/analysis/adapters/python.js +475 -0
  58. package/dist/src/analysis/adapters/python.js.map +1 -0
  59. package/dist/src/analysis/adapters/rust.d.ts +116 -0
  60. package/dist/src/analysis/adapters/rust.d.ts.map +1 -0
  61. package/dist/src/analysis/adapters/rust.js +476 -0
  62. package/dist/src/analysis/adapters/rust.js.map +1 -0
  63. package/dist/src/analysis/adapters/typescript.d.ts +68 -0
  64. package/dist/src/analysis/adapters/typescript.d.ts.map +1 -0
  65. package/dist/src/analysis/adapters/typescript.js +79 -0
  66. package/dist/src/analysis/adapters/typescript.js.map +1 -0
  67. package/dist/src/analysis/aggregator.d.ts +193 -0
  68. package/dist/src/analysis/aggregator.d.ts.map +1 -0
  69. package/dist/src/analysis/aggregator.js +283 -0
  70. package/dist/src/analysis/aggregator.js.map +1 -0
  71. package/dist/src/analysis/cache.d.ts +180 -0
  72. package/dist/src/analysis/cache.d.ts.map +1 -0
  73. package/dist/src/analysis/cache.js +279 -0
  74. package/dist/src/analysis/cache.js.map +1 -0
  75. package/dist/src/analysis/file-streamer.d.ts +136 -0
  76. package/dist/src/analysis/file-streamer.d.ts.map +1 -0
  77. package/dist/src/analysis/file-streamer.js +291 -0
  78. package/dist/src/analysis/file-streamer.js.map +1 -0
  79. package/dist/src/analysis/incremental-parser.d.ts +186 -0
  80. package/dist/src/analysis/incremental-parser.d.ts.map +1 -0
  81. package/dist/src/analysis/incremental-parser.js +291 -0
  82. package/dist/src/analysis/incremental-parser.js.map +1 -0
  83. package/dist/src/analysis/incremental.d.ts +186 -0
  84. package/dist/src/analysis/incremental.d.ts.map +1 -0
  85. package/dist/src/analysis/incremental.js +247 -0
  86. package/dist/src/analysis/incremental.js.map +1 -0
  87. package/dist/src/analysis/index.d.ts +25 -3
  88. package/dist/src/analysis/index.d.ts.map +1 -1
  89. package/dist/src/analysis/index.js +45 -3
  90. package/dist/src/analysis/index.js.map +1 -1
  91. package/dist/src/analysis/language-detector.d.ts +92 -0
  92. package/dist/src/analysis/language-detector.d.ts.map +1 -0
  93. package/dist/src/analysis/language-detector.js +602 -0
  94. package/dist/src/analysis/language-detector.js.map +1 -0
  95. package/dist/src/analysis/memory-monitor.d.ts +199 -0
  96. package/dist/src/analysis/memory-monitor.d.ts.map +1 -0
  97. package/dist/src/analysis/memory-monitor.js +271 -0
  98. package/dist/src/analysis/memory-monitor.js.map +1 -0
  99. package/dist/src/analysis/metrics.d.ts +300 -0
  100. package/dist/src/analysis/metrics.d.ts.map +1 -0
  101. package/dist/src/analysis/metrics.js +537 -0
  102. package/dist/src/analysis/metrics.js.map +1 -0
  103. package/dist/src/analysis/router.d.ts +264 -0
  104. package/dist/src/analysis/router.d.ts.map +1 -0
  105. package/dist/src/analysis/router.js +398 -0
  106. package/dist/src/analysis/router.js.map +1 -0
  107. package/dist/src/analysis/tree-cache.d.ts +208 -0
  108. package/dist/src/analysis/tree-cache.d.ts.map +1 -0
  109. package/dist/src/analysis/tree-cache.js +288 -0
  110. package/dist/src/analysis/tree-cache.js.map +1 -0
  111. package/dist/src/analysis/tree-sitter/manager.d.ts +141 -0
  112. package/dist/src/analysis/tree-sitter/manager.d.ts.map +1 -0
  113. package/dist/src/analysis/tree-sitter/manager.js +239 -0
  114. package/dist/src/analysis/tree-sitter/manager.js.map +1 -0
  115. package/dist/src/analysis/types.d.ts +69 -6
  116. package/dist/src/analysis/types.d.ts.map +1 -1
  117. package/dist/src/analysis/types.js +23 -2
  118. package/dist/src/analysis/types.js.map +1 -1
  119. package/dist/src/analysis/worker-pool.d.ts +141 -0
  120. package/dist/src/analysis/worker-pool.d.ts.map +1 -0
  121. package/dist/src/analysis/worker-pool.js +418 -0
  122. package/dist/src/analysis/worker-pool.js.map +1 -0
  123. package/dist/src/analytics/schema.d.ts +1 -1
  124. package/dist/src/analytics/schema.d.ts.map +1 -1
  125. package/dist/src/analytics/schema.js +72 -0
  126. package/dist/src/analytics/schema.js.map +1 -1
  127. package/dist/src/api/cache.d.ts +24 -1
  128. package/dist/src/api/cache.d.ts.map +1 -1
  129. package/dist/src/api/cache.js +50 -2
  130. package/dist/src/api/cache.js.map +1 -1
  131. package/dist/src/api/client.d.ts +132 -2
  132. package/dist/src/api/client.d.ts.map +1 -1
  133. package/dist/src/api/client.js +214 -18
  134. package/dist/src/api/client.js.map +1 -1
  135. package/dist/src/api/index.d.ts +2 -0
  136. package/dist/src/api/index.d.ts.map +1 -1
  137. package/dist/src/api/index.js +7 -0
  138. package/dist/src/api/index.js.map +1 -1
  139. package/dist/src/api/types.d.ts +251 -0
  140. package/dist/src/api/types.d.ts.map +1 -0
  141. package/dist/src/api/types.js +9 -0
  142. package/dist/src/api/types.js.map +1 -0
  143. package/dist/src/benchmarks/memory/MemoryProfiler.d.ts.map +1 -1
  144. package/dist/src/benchmarks/memory/MemoryProfiler.js.map +1 -1
  145. package/dist/src/embeddings/index.d.ts.map +1 -1
  146. package/dist/src/embeddings/index.js.map +1 -1
  147. package/dist/src/errors.d.ts +1 -0
  148. package/dist/src/errors.d.ts.map +1 -1
  149. package/dist/src/errors.js +1 -0
  150. package/dist/src/errors.js.map +1 -1
  151. package/dist/src/index.d.ts +3 -3
  152. package/dist/src/index.d.ts.map +1 -1
  153. package/dist/src/index.js +4 -4
  154. package/dist/src/index.js.map +1 -1
  155. package/dist/src/repositories/IndexerRepository.d.ts.map +1 -1
  156. package/dist/src/repositories/IndexerRepository.js +1 -0
  157. package/dist/src/repositories/IndexerRepository.js.map +1 -1
  158. package/dist/src/repositories/SkillRepository.d.ts.map +1 -1
  159. package/dist/src/repositories/SkillRepository.js +1 -0
  160. package/dist/src/repositories/SkillRepository.js.map +1 -1
  161. package/dist/src/repositories/quarantine/QuarantineRepository.d.ts.map +1 -1
  162. package/dist/src/repositories/quarantine/QuarantineRepository.js.map +1 -1
  163. package/dist/src/repositories/quarantine/query-builder.d.ts.map +1 -1
  164. package/dist/src/repositories/quarantine/query-builder.js +1 -1
  165. package/dist/src/repositories/quarantine/query-builder.js.map +1 -1
  166. package/dist/src/scripts/__tests__/scan-imported-skills.test.js +3 -3
  167. package/dist/src/scripts/__tests__/scan-imported-skills.test.js.map +1 -1
  168. package/dist/src/scripts/github-import/index.js.map +1 -1
  169. package/dist/src/scripts/import-github-skills.js +1 -1
  170. package/dist/src/scripts/import-github-skills.js.map +1 -1
  171. package/dist/src/scripts/skill-scanner/reporter.d.ts.map +1 -1
  172. package/dist/src/scripts/skill-scanner/reporter.js.map +1 -1
  173. package/dist/src/scripts/skill-scanner/scanner.d.ts.map +1 -1
  174. package/dist/src/scripts/skill-scanner/scanner.js.map +1 -1
  175. package/dist/src/scripts/skill-scanner/trust-scorer.d.ts.map +1 -1
  176. package/dist/src/scripts/skill-scanner/trust-scorer.js.map +1 -1
  177. package/dist/src/scripts/validation/index.js +1 -2
  178. package/dist/src/scripts/validation/index.js.map +1 -1
  179. package/dist/src/scripts/validation/pipeline.d.ts.map +1 -1
  180. package/dist/src/scripts/validation/pipeline.js.map +1 -1
  181. package/dist/src/scripts/validation/types.d.ts +2 -2
  182. package/dist/src/security/scanner/SecurityScanner.d.ts.map +1 -1
  183. package/dist/src/security/scanner/SecurityScanner.js.map +1 -1
  184. package/dist/src/services/SearchService.d.ts.map +1 -1
  185. package/dist/src/services/SearchService.js +1 -0
  186. package/dist/src/services/SearchService.js.map +1 -1
  187. package/dist/src/session/SessionHealthMonitor.d.ts +1 -1
  188. package/dist/src/session/SessionHealthMonitor.d.ts.map +1 -1
  189. package/dist/src/session/SessionHealthMonitor.js +1 -1
  190. package/dist/src/session/SessionHealthMonitor.js.map +1 -1
  191. package/dist/src/telemetry/index.d.ts +1 -1
  192. package/dist/src/telemetry/index.d.ts.map +1 -1
  193. package/dist/src/telemetry/index.js +2 -2
  194. package/dist/src/telemetry/index.js.map +1 -1
  195. package/dist/src/telemetry/posthog.d.ts +27 -5
  196. package/dist/src/telemetry/posthog.d.ts.map +1 -1
  197. package/dist/src/telemetry/posthog.js +20 -5
  198. package/dist/src/telemetry/posthog.js.map +1 -1
  199. package/dist/src/types/skill.d.ts +3 -0
  200. package/dist/src/types/skill.d.ts.map +1 -1
  201. package/dist/src/types.d.ts +2 -1
  202. package/dist/src/types.d.ts.map +1 -1
  203. package/dist/src/types.js +2 -2
  204. package/dist/src/types.js.map +1 -1
  205. package/dist/tests/adapters-factory.test.d.ts +13 -0
  206. package/dist/tests/adapters-factory.test.d.ts.map +1 -0
  207. package/dist/tests/adapters-factory.test.js +308 -0
  208. package/dist/tests/adapters-factory.test.js.map +1 -0
  209. package/dist/tests/adapters-java.test.d.ts +13 -0
  210. package/dist/tests/adapters-java.test.d.ts.map +1 -0
  211. package/dist/tests/adapters-java.test.js +925 -0
  212. package/dist/tests/adapters-java.test.js.map +1 -0
  213. package/dist/tests/api/client.validation.test.d.ts +7 -0
  214. package/dist/tests/api/client.validation.test.d.ts.map +1 -0
  215. package/dist/tests/api/client.validation.test.js +183 -0
  216. package/dist/tests/api/client.validation.test.js.map +1 -0
  217. package/dist/tests/language-detector.test.d.ts +13 -0
  218. package/dist/tests/language-detector.test.d.ts.map +1 -0
  219. package/dist/tests/language-detector.test.js +674 -0
  220. package/dist/tests/language-detector.test.js.map +1 -0
  221. package/dist/tests/telemetry/posthog.test.d.ts +13 -0
  222. package/dist/tests/telemetry/posthog.test.d.ts.map +1 -0
  223. package/dist/tests/telemetry/posthog.test.js +600 -0
  224. package/dist/tests/telemetry/posthog.test.js.map +1 -0
  225. package/package.json +5 -6
  226. package/dist/src/security/RateLimiter.d.ts +0 -337
  227. package/dist/src/security/RateLimiter.d.ts.map +0 -1
  228. package/dist/src/security/RateLimiter.js +0 -782
  229. package/dist/src/security/RateLimiter.js.map +0 -1
  230. package/dist/src/security/scanner.d.ts +0 -151
  231. package/dist/src/security/scanner.d.ts.map +0 -1
  232. package/dist/src/security/scanner.js +0 -599
  233. package/dist/src/security/scanner.js.map +0 -1
@@ -0,0 +1,676 @@
1
+ /**
2
+ * SMI-1306: Rust Language Adapter Tests
3
+ *
4
+ * Comprehensive tests for the Rust adapter including:
5
+ * - Import extraction (use statements, extern crate)
6
+ * - Export detection (pub visibility)
7
+ * - Function extraction (sync, async, with attributes)
8
+ * - Cargo.toml parsing
9
+ * - Framework detection rules
10
+ */
11
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
12
+ import { RustAdapter, parseCargoToml } from '../rust.js';
13
+ describe('RustAdapter', () => {
14
+ let adapter;
15
+ beforeEach(() => {
16
+ adapter = new RustAdapter();
17
+ });
18
+ afterEach(() => {
19
+ adapter.dispose();
20
+ });
21
+ describe('canHandle', () => {
22
+ it('handles .rs files', () => {
23
+ expect(adapter.canHandle('main.rs')).toBe(true);
24
+ expect(adapter.canHandle('lib.rs')).toBe(true);
25
+ expect(adapter.canHandle('path/to/module.rs')).toBe(true);
26
+ });
27
+ it('does not handle non-Rust files', () => {
28
+ expect(adapter.canHandle('main.ts')).toBe(false);
29
+ expect(adapter.canHandle('main.py')).toBe(false);
30
+ expect(adapter.canHandle('main.go')).toBe(false);
31
+ expect(adapter.canHandle('main.java')).toBe(false);
32
+ expect(adapter.canHandle('Cargo.toml')).toBe(false);
33
+ });
34
+ it('handles case-insensitive extensions', () => {
35
+ expect(adapter.canHandle('main.RS')).toBe(true);
36
+ expect(adapter.canHandle('main.Rs')).toBe(true);
37
+ });
38
+ });
39
+ describe('parseFile - imports', () => {
40
+ it('extracts simple use statement', () => {
41
+ const content = `
42
+ use std::io;
43
+ `;
44
+ const result = adapter.parseFile(content, 'main.rs');
45
+ expect(result.imports).toHaveLength(1);
46
+ expect(result.imports[0]).toMatchObject({
47
+ module: 'std::io',
48
+ namedImports: ['io'],
49
+ isTypeOnly: false,
50
+ sourceFile: 'main.rs',
51
+ });
52
+ });
53
+ it('extracts use with alias', () => {
54
+ const content = `
55
+ use std::collections::HashMap as Map;
56
+ `;
57
+ const result = adapter.parseFile(content, 'main.rs');
58
+ expect(result.imports).toHaveLength(1);
59
+ expect(result.imports[0]).toMatchObject({
60
+ module: 'std::collections::HashMap',
61
+ defaultImport: 'Map',
62
+ });
63
+ });
64
+ it('extracts glob import', () => {
65
+ const content = `
66
+ use std::prelude::*;
67
+ `;
68
+ const result = adapter.parseFile(content, 'main.rs');
69
+ expect(result.imports).toHaveLength(1);
70
+ expect(result.imports[0]).toMatchObject({
71
+ module: 'std::prelude',
72
+ namespaceImport: '*',
73
+ namedImports: [],
74
+ });
75
+ });
76
+ it('extracts grouped imports', () => {
77
+ const content = `
78
+ use std::{io, fs, path};
79
+ `;
80
+ const result = adapter.parseFile(content, 'main.rs');
81
+ expect(result.imports).toHaveLength(1);
82
+ expect(result.imports[0]).toMatchObject({
83
+ module: 'std',
84
+ namedImports: ['io', 'fs', 'path'],
85
+ });
86
+ });
87
+ it('extracts crate-relative imports', () => {
88
+ const content = `
89
+ use crate::models::User;
90
+ use crate::utils::helpers;
91
+ `;
92
+ const result = adapter.parseFile(content, 'main.rs');
93
+ expect(result.imports).toHaveLength(2);
94
+ expect(result.imports[0].module).toBe('crate::models::User');
95
+ expect(result.imports[1].module).toBe('crate::utils::helpers');
96
+ });
97
+ it('extracts super imports', () => {
98
+ const content = `
99
+ use super::parent_module;
100
+ use super::super::grandparent;
101
+ `;
102
+ const result = adapter.parseFile(content, 'child.rs');
103
+ expect(result.imports).toHaveLength(2);
104
+ expect(result.imports[0].module).toBe('super::parent_module');
105
+ });
106
+ it('extracts self imports', () => {
107
+ const content = `
108
+ use self::submodule;
109
+ `;
110
+ const result = adapter.parseFile(content, 'mod.rs');
111
+ expect(result.imports).toHaveLength(1);
112
+ expect(result.imports[0].module).toBe('self::submodule');
113
+ });
114
+ it('extracts extern crate', () => {
115
+ const content = `
116
+ extern crate serde;
117
+ extern crate serde_json as json;
118
+ `;
119
+ const result = adapter.parseFile(content, 'lib.rs');
120
+ expect(result.imports).toHaveLength(2);
121
+ expect(result.imports[0]).toMatchObject({
122
+ module: 'serde',
123
+ namedImports: [],
124
+ });
125
+ expect(result.imports[1]).toMatchObject({
126
+ module: 'serde_json',
127
+ defaultImport: 'json',
128
+ });
129
+ });
130
+ it('handles grouped imports with self', () => {
131
+ const content = `
132
+ use actix_web::{self, web, App, HttpServer};
133
+ `;
134
+ const result = adapter.parseFile(content, 'main.rs');
135
+ expect(result.imports).toHaveLength(1);
136
+ expect(result.imports[0].namedImports).toContain('web');
137
+ expect(result.imports[0].namedImports).toContain('App');
138
+ expect(result.imports[0].namedImports).toContain('HttpServer');
139
+ // self should be filtered out
140
+ expect(result.imports[0].namedImports).not.toContain('self');
141
+ });
142
+ });
143
+ describe('parseFile - exports', () => {
144
+ it('detects pub struct', () => {
145
+ const content = `
146
+ pub struct User {
147
+ id: u64,
148
+ name: String,
149
+ }
150
+
151
+ struct PrivateData {
152
+ secret: String,
153
+ }
154
+ `;
155
+ const result = adapter.parseFile(content, 'models.rs');
156
+ expect(result.exports).toHaveLength(1);
157
+ expect(result.exports[0]).toMatchObject({
158
+ name: 'User',
159
+ kind: 'struct',
160
+ isDefault: false,
161
+ sourceFile: 'models.rs',
162
+ visibility: 'public',
163
+ });
164
+ });
165
+ it('detects pub enum', () => {
166
+ const content = `
167
+ pub enum Status {
168
+ Active,
169
+ Inactive,
170
+ }
171
+
172
+ enum InternalState {
173
+ Running,
174
+ Stopped,
175
+ }
176
+ `;
177
+ const result = adapter.parseFile(content, 'types.rs');
178
+ expect(result.exports).toHaveLength(1);
179
+ expect(result.exports[0]).toMatchObject({
180
+ name: 'Status',
181
+ kind: 'enum',
182
+ visibility: 'public',
183
+ });
184
+ });
185
+ it('detects pub trait', () => {
186
+ const content = `
187
+ pub trait Serialize {
188
+ fn serialize(&self) -> Vec<u8>;
189
+ }
190
+
191
+ trait InternalFormat {
192
+ fn format(&self) -> String;
193
+ }
194
+ `;
195
+ const result = adapter.parseFile(content, 'traits.rs');
196
+ expect(result.exports).toHaveLength(1);
197
+ expect(result.exports[0]).toMatchObject({
198
+ name: 'Serialize',
199
+ kind: 'trait',
200
+ visibility: 'public',
201
+ });
202
+ });
203
+ it('detects pub fn', () => {
204
+ const content = `
205
+ pub fn public_function() {
206
+ }
207
+
208
+ fn private_function() {
209
+ }
210
+
211
+ pub async fn async_public() {
212
+ }
213
+ `;
214
+ const result = adapter.parseFile(content, 'lib.rs');
215
+ expect(result.exports).toHaveLength(2);
216
+ expect(result.exports.map((e) => e.name)).toContain('public_function');
217
+ expect(result.exports.map((e) => e.name)).toContain('async_public');
218
+ expect(result.exports.map((e) => e.name)).not.toContain('private_function');
219
+ });
220
+ it('detects pub mod', () => {
221
+ const content = `
222
+ pub mod api;
223
+ pub mod handlers;
224
+ mod internal;
225
+ `;
226
+ const result = adapter.parseFile(content, 'lib.rs');
227
+ expect(result.exports).toHaveLength(2);
228
+ expect(result.exports.map((e) => e.name)).toContain('api');
229
+ expect(result.exports.map((e) => e.name)).toContain('handlers');
230
+ expect(result.exports.every((e) => e.kind === 'module')).toBe(true);
231
+ });
232
+ it('detects pub type', () => {
233
+ const content = `
234
+ pub type Result<T> = std::result::Result<T, Error>;
235
+ type InternalResult = Result<()>;
236
+ `;
237
+ const result = adapter.parseFile(content, 'types.rs');
238
+ expect(result.exports).toHaveLength(1);
239
+ expect(result.exports[0]).toMatchObject({
240
+ name: 'Result',
241
+ kind: 'type',
242
+ visibility: 'public',
243
+ });
244
+ });
245
+ it('detects pub const and pub static', () => {
246
+ const content = `
247
+ pub const MAX_SIZE: usize = 1024;
248
+ pub static GLOBAL_CONFIG: Config = Config::new();
249
+ const INTERNAL_LIMIT: usize = 512;
250
+ static INTERNAL_STATE: State = State::new();
251
+ `;
252
+ const result = adapter.parseFile(content, 'config.rs');
253
+ expect(result.exports).toHaveLength(2);
254
+ expect(result.exports.map((e) => e.name)).toContain('MAX_SIZE');
255
+ expect(result.exports.map((e) => e.name)).toContain('GLOBAL_CONFIG');
256
+ expect(result.exports.every((e) => e.kind === 'variable')).toBe(true);
257
+ });
258
+ it('handles pub(crate) visibility', () => {
259
+ const content = `
260
+ pub(crate) struct InternalStruct {
261
+ field: u32,
262
+ }
263
+
264
+ pub(crate) fn internal_function() {
265
+ }
266
+ `;
267
+ const result = adapter.parseFile(content, 'internal.rs');
268
+ // pub(crate) is still considered an export for analysis purposes
269
+ expect(result.exports).toHaveLength(2);
270
+ expect(result.exports.map((e) => e.name)).toContain('InternalStruct');
271
+ expect(result.exports.map((e) => e.name)).toContain('internal_function');
272
+ });
273
+ });
274
+ describe('parseFile - functions', () => {
275
+ it('extracts function with no parameters', () => {
276
+ const content = `
277
+ fn do_something() {
278
+ }
279
+ `;
280
+ const result = adapter.parseFile(content, 'main.rs');
281
+ expect(result.functions).toHaveLength(1);
282
+ expect(result.functions[0]).toMatchObject({
283
+ name: 'do_something',
284
+ parameterCount: 0,
285
+ isAsync: false,
286
+ isExported: false,
287
+ sourceFile: 'main.rs',
288
+ line: 2,
289
+ });
290
+ });
291
+ it('extracts function with parameters', () => {
292
+ const content = `
293
+ fn add(a: i32, b: i32) -> i32 {
294
+ a + b
295
+ }
296
+
297
+ fn greet(name: &str, times: u32) {
298
+ }
299
+ `;
300
+ const result = adapter.parseFile(content, 'math.rs');
301
+ expect(result.functions).toHaveLength(2);
302
+ expect(result.functions[0]).toMatchObject({
303
+ name: 'add',
304
+ parameterCount: 2,
305
+ isExported: false,
306
+ });
307
+ expect(result.functions[1]).toMatchObject({
308
+ name: 'greet',
309
+ parameterCount: 2,
310
+ });
311
+ });
312
+ it('extracts method with self parameter', () => {
313
+ const content = `
314
+ impl User {
315
+ fn get_name(&self) -> &str {
316
+ &self.name
317
+ }
318
+
319
+ fn set_name(&mut self, name: String) {
320
+ self.name = name;
321
+ }
322
+
323
+ fn new(name: String) -> Self {
324
+ Self { name }
325
+ }
326
+ }
327
+ `;
328
+ const result = adapter.parseFile(content, 'user.rs');
329
+ expect(result.functions).toHaveLength(3);
330
+ // get_name: &self is not counted
331
+ expect(result.functions[0]).toMatchObject({
332
+ name: 'get_name',
333
+ parameterCount: 0,
334
+ });
335
+ // set_name: &mut self is not counted, name is
336
+ expect(result.functions[1]).toMatchObject({
337
+ name: 'set_name',
338
+ parameterCount: 1,
339
+ });
340
+ // new: no self, name is counted
341
+ expect(result.functions[2]).toMatchObject({
342
+ name: 'new',
343
+ parameterCount: 1,
344
+ });
345
+ });
346
+ it('extracts async functions', () => {
347
+ const content = `
348
+ async fn fetch_data() {
349
+ }
350
+
351
+ pub async fn handle_request(req: Request) -> Response {
352
+ }
353
+ `;
354
+ const result = adapter.parseFile(content, 'handlers.rs');
355
+ expect(result.functions).toHaveLength(2);
356
+ expect(result.functions[0]).toMatchObject({
357
+ name: 'fetch_data',
358
+ isAsync: true,
359
+ isExported: false,
360
+ });
361
+ expect(result.functions[1]).toMatchObject({
362
+ name: 'handle_request',
363
+ isAsync: true,
364
+ isExported: true,
365
+ parameterCount: 1,
366
+ });
367
+ });
368
+ it('extracts generic functions', () => {
369
+ const content = `
370
+ fn process<T: Clone>(item: T) -> T {
371
+ item.clone()
372
+ }
373
+
374
+ fn transform<T, U>(input: T, f: impl Fn(T) -> U) -> U {
375
+ f(input)
376
+ }
377
+ `;
378
+ const result = adapter.parseFile(content, 'generic.rs');
379
+ expect(result.functions).toHaveLength(2);
380
+ expect(result.functions[0]).toMatchObject({
381
+ name: 'process',
382
+ parameterCount: 1,
383
+ });
384
+ expect(result.functions[1]).toMatchObject({
385
+ name: 'transform',
386
+ parameterCount: 2,
387
+ });
388
+ });
389
+ it('detects attributes on functions', () => {
390
+ const content = `
391
+ #[test]
392
+ fn test_addition() {
393
+ assert_eq!(2 + 2, 4);
394
+ }
395
+
396
+ #[tokio::test]
397
+ async fn test_async() {
398
+ }
399
+
400
+ #[derive(Debug)]
401
+ #[inline]
402
+ pub fn optimized() {
403
+ }
404
+ `;
405
+ const result = adapter.parseFile(content, 'tests.rs');
406
+ expect(result.functions).toHaveLength(3);
407
+ expect(result.functions[0].attributes).toEqual(['test']);
408
+ expect(result.functions[1].attributes).toEqual(['tokio::test']);
409
+ expect(result.functions[2].attributes).toEqual(['derive', 'inline']);
410
+ });
411
+ it('correctly reports line numbers', () => {
412
+ const content = `fn first() {}
413
+
414
+ fn second() {}
415
+
416
+ fn third() {}
417
+ `;
418
+ const result = adapter.parseFile(content, 'lines.rs');
419
+ expect(result.functions).toHaveLength(3);
420
+ expect(result.functions[0].line).toBe(1);
421
+ expect(result.functions[1].line).toBe(3);
422
+ expect(result.functions[2].line).toBe(5);
423
+ });
424
+ });
425
+ describe('parseFile - complex cases', () => {
426
+ it('parses a complete Rust file', () => {
427
+ const content = `
428
+ use std::io::{self, Read, Write};
429
+ use serde::{Deserialize, Serialize};
430
+ use actix_web::{web, App, HttpServer};
431
+
432
+ pub struct AppConfig {
433
+ port: u16,
434
+ host: String,
435
+ }
436
+
437
+ pub trait Handler {
438
+ fn handle(&self, req: Request) -> Response;
439
+ }
440
+
441
+ #[derive(Debug, Clone)]
442
+ pub struct Request {
443
+ pub method: String,
444
+ pub path: String,
445
+ }
446
+
447
+ pub async fn start_server(config: AppConfig) -> io::Result<()> {
448
+ HttpServer::new(|| {
449
+ App::new()
450
+ })
451
+ .bind((config.host, config.port))?
452
+ .run()
453
+ .await
454
+ }
455
+
456
+ fn internal_helper() {
457
+ }
458
+
459
+ pub const DEFAULT_PORT: u16 = 8080;
460
+ `;
461
+ const result = adapter.parseFile(content, 'server.rs');
462
+ // Imports
463
+ expect(result.imports).toHaveLength(3);
464
+ expect(result.imports.map((i) => i.module)).toContain('std::io');
465
+ expect(result.imports.map((i) => i.module)).toContain('serde');
466
+ expect(result.imports.map((i) => i.module)).toContain('actix_web');
467
+ // Exports (pub items)
468
+ expect(result.exports.map((e) => e.name)).toContain('AppConfig');
469
+ expect(result.exports.map((e) => e.name)).toContain('Handler');
470
+ expect(result.exports.map((e) => e.name)).toContain('Request');
471
+ expect(result.exports.map((e) => e.name)).toContain('start_server');
472
+ expect(result.exports.map((e) => e.name)).toContain('DEFAULT_PORT');
473
+ expect(result.exports.map((e) => e.name)).not.toContain('internal_helper');
474
+ // Functions
475
+ const startServer = result.functions.find((f) => f.name === 'start_server');
476
+ expect(startServer).toMatchObject({
477
+ isAsync: true,
478
+ isExported: true,
479
+ parameterCount: 1,
480
+ });
481
+ const helper = result.functions.find((f) => f.name === 'internal_helper');
482
+ expect(helper).toMatchObject({
483
+ isAsync: false,
484
+ isExported: false,
485
+ parameterCount: 0,
486
+ });
487
+ });
488
+ });
489
+ describe('getFrameworkRules', () => {
490
+ it('includes Actix framework detection', () => {
491
+ const rules = adapter.getFrameworkRules();
492
+ const actix = rules.find((r) => r.name === 'Actix');
493
+ expect(actix).toBeDefined();
494
+ expect(actix?.depIndicators).toContain('actix-web');
495
+ expect(actix?.importIndicators).toContain('actix_web');
496
+ });
497
+ it('includes Tokio detection', () => {
498
+ const rules = adapter.getFrameworkRules();
499
+ const tokio = rules.find((r) => r.name === 'Tokio');
500
+ expect(tokio).toBeDefined();
501
+ expect(tokio?.depIndicators).toContain('tokio');
502
+ });
503
+ it('includes Serde detection', () => {
504
+ const rules = adapter.getFrameworkRules();
505
+ const serde = rules.find((r) => r.name === 'Serde');
506
+ expect(serde).toBeDefined();
507
+ expect(serde?.depIndicators).toContain('serde');
508
+ expect(serde?.depIndicators).toContain('serde_json');
509
+ });
510
+ it('includes Axum detection', () => {
511
+ const rules = adapter.getFrameworkRules();
512
+ const axum = rules.find((r) => r.name === 'Axum');
513
+ expect(axum).toBeDefined();
514
+ expect(axum?.importIndicators).toContain('axum');
515
+ });
516
+ it('includes SQLx detection', () => {
517
+ const rules = adapter.getFrameworkRules();
518
+ const sqlx = rules.find((r) => r.name === 'SQLx');
519
+ expect(sqlx).toBeDefined();
520
+ expect(sqlx?.depIndicators).toContain('sqlx');
521
+ });
522
+ it('includes Clap detection', () => {
523
+ const rules = adapter.getFrameworkRules();
524
+ const clap = rules.find((r) => r.name === 'Clap');
525
+ expect(clap).toBeDefined();
526
+ expect(clap?.depIndicators).toContain('clap');
527
+ });
528
+ });
529
+ describe('parseIncremental', () => {
530
+ it('returns same result as parseFile', () => {
531
+ const content = `
532
+ use std::io;
533
+
534
+ pub fn hello() {
535
+ println!("Hello");
536
+ }
537
+ `;
538
+ const parseResult = adapter.parseFile(content, 'main.rs');
539
+ const incrementalResult = adapter.parseIncremental(content, 'main.rs');
540
+ expect(incrementalResult).toEqual(parseResult);
541
+ });
542
+ });
543
+ });
544
+ describe('parseCargoToml', () => {
545
+ it('extracts regular dependencies', () => {
546
+ const content = `
547
+ [package]
548
+ name = "my-app"
549
+ version = "0.1.0"
550
+
551
+ [dependencies]
552
+ serde = "1.0"
553
+ tokio = "1.28"
554
+ `;
555
+ const result = parseCargoToml(content);
556
+ expect(result).toHaveLength(2);
557
+ expect(result).toContainEqual({ name: 'serde', version: '1.0', isDev: false });
558
+ expect(result).toContainEqual({ name: 'tokio', version: '1.28', isDev: false });
559
+ });
560
+ it('extracts dev dependencies', () => {
561
+ const content = `
562
+ [dependencies]
563
+ serde = "1.0"
564
+
565
+ [dev-dependencies]
566
+ mockall = "0.11"
567
+ criterion = "0.5"
568
+ `;
569
+ const result = parseCargoToml(content);
570
+ expect(result).toHaveLength(3);
571
+ expect(result.find((d) => d.name === 'serde')?.isDev).toBe(false);
572
+ expect(result.find((d) => d.name === 'mockall')?.isDev).toBe(true);
573
+ expect(result.find((d) => d.name === 'criterion')?.isDev).toBe(true);
574
+ });
575
+ it('handles table-format dependencies', () => {
576
+ const content = `
577
+ [dependencies]
578
+ serde = { version = "1.0", features = ["derive"] }
579
+ tokio = { version = "1.28", features = ["full"] }
580
+ `;
581
+ const result = parseCargoToml(content);
582
+ expect(result).toHaveLength(2);
583
+ expect(result).toContainEqual({ name: 'serde', version: '1.0', isDev: false });
584
+ expect(result).toContainEqual({ name: 'tokio', version: '1.28', isDev: false });
585
+ });
586
+ it('handles git/path dependencies', () => {
587
+ const content = `
588
+ [dependencies]
589
+ local-lib = { path = "../local-lib" }
590
+ git-lib = { git = "https://github.com/user/repo" }
591
+ `;
592
+ const result = parseCargoToml(content);
593
+ expect(result).toHaveLength(2);
594
+ expect(result.find((d) => d.name === 'local-lib')).toMatchObject({
595
+ name: 'local-lib',
596
+ version: 'git/path',
597
+ isDev: false,
598
+ });
599
+ });
600
+ it('handles build dependencies', () => {
601
+ const content = `
602
+ [dependencies]
603
+ serde = "1.0"
604
+
605
+ [build-dependencies]
606
+ cc = "1.0"
607
+ `;
608
+ const result = parseCargoToml(content);
609
+ // Build dependencies are not marked as dev
610
+ expect(result).toHaveLength(2);
611
+ expect(result.find((d) => d.name === 'cc')?.isDev).toBe(false);
612
+ });
613
+ it('parses complete Cargo.toml', () => {
614
+ const content = `
615
+ [package]
616
+ name = "my-awesome-app"
617
+ version = "0.1.0"
618
+ edition = "2021"
619
+
620
+ [dependencies]
621
+ actix-web = "4.0"
622
+ serde = { version = "1.0", features = ["derive"] }
623
+ sqlx = { version = "0.7", features = ["postgres", "runtime-tokio"] }
624
+ tokio = { version = "1.28", features = ["full"] }
625
+
626
+ [dev-dependencies]
627
+ mockall = "0.11"
628
+ tokio-test = "0.4"
629
+
630
+ [build-dependencies]
631
+ prost-build = "0.12"
632
+ `;
633
+ const result = parseCargoToml(content);
634
+ expect(result.filter((d) => !d.isDev).length).toBe(5);
635
+ expect(result.filter((d) => d.isDev).length).toBe(2);
636
+ expect(result.map((d) => d.name)).toContain('actix-web');
637
+ expect(result.map((d) => d.name)).toContain('mockall');
638
+ });
639
+ it('handles empty Cargo.toml', () => {
640
+ const content = `
641
+ [package]
642
+ name = "minimal"
643
+ version = "0.1.0"
644
+ `;
645
+ const result = parseCargoToml(content);
646
+ expect(result).toHaveLength(0);
647
+ });
648
+ it('ignores comments', () => {
649
+ const content = `
650
+ [dependencies]
651
+ # This is a comment
652
+ serde = "1.0"
653
+ # tokio = "1.28"
654
+ `;
655
+ const result = parseCargoToml(content);
656
+ expect(result).toHaveLength(1);
657
+ expect(result[0].name).toBe('serde');
658
+ });
659
+ it('stops parsing at other sections', () => {
660
+ const content = `
661
+ [dependencies]
662
+ serde = "1.0"
663
+
664
+ [features]
665
+ default = ["full"]
666
+ full = []
667
+
668
+ [profile.release]
669
+ opt-level = 3
670
+ `;
671
+ const result = parseCargoToml(content);
672
+ expect(result).toHaveLength(1);
673
+ expect(result[0].name).toBe('serde');
674
+ });
675
+ });
676
+ //# sourceMappingURL=rust.test.js.map