1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4use crate::{
5 CacheInfo, CallNode, FileInfo, LocationInfo, SymbolInfo, WorkspaceInfo, DEFAULT_HEAD_LIMIT,
6};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
10#[serde(tag = "type", rename_all = "snake_case")]
11pub enum StreamMessage {
12 Symbol(SymbolInfo),
13 File(FileInfo),
14 ExcludedDir { path: String },
15 Done(StreamDone),
16 Error { message: String },
17}
18
19#[derive(Debug, Clone, Default, Serialize, Deserialize)]
20pub struct StreamDone {
21 #[serde(skip_serializing_if = "Option::is_none")]
22 pub warning: Option<String>,
23 pub truncated: bool,
24 pub total_count: u32,
25 #[serde(skip_serializing_if = "Option::is_none")]
26 pub profiling: Option<ProfilingData>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct FunctionStats {
31 pub name: String,
32 pub calls: u32,
33 pub total_us: u64,
34 pub avg_us: u64,
35 pub p90_us: u64,
36 pub max_us: u64,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct SpanNode {
41 pub name: String,
42 pub self_us: u64,
43 pub total_us: u64,
44 pub calls: u32,
45 pub children: Vec<SpanNode>,
46 pub is_parallel: bool,
47 #[serde(default, skip_serializing_if = "Vec::is_empty")]
48 pub properties: Vec<(String, String)>,
49}
50
51#[derive(Debug, Clone, Default, Serialize, Deserialize)]
52pub struct SpanTree {
53 pub roots: Vec<SpanNode>,
54 pub total_us: u64,
55 pub functions: Vec<FunctionStats>,
56}
57
58#[derive(Debug, Clone, Default, Serialize, Deserialize)]
59pub struct CacheStats {
60 pub symbol_hits: u32,
61 pub symbol_misses: u32,
62 pub hover_hits: u32,
63 pub hover_misses: u32,
64}
65
66impl CacheStats {
67 pub fn symbol_hit_rate(&self) -> f64 {
68 let total = self.symbol_hits + self.symbol_misses;
69 if total == 0 {
70 0.0
71 } else {
72 self.symbol_hits as f64 / total as f64 * 100.0
73 }
74 }
75
76 pub fn hover_hit_rate(&self) -> f64 {
77 let total = self.hover_hits + self.hover_misses;
78 if total == 0 {
79 0.0
80 } else {
81 self.hover_hits as f64 / total as f64 * 100.0
82 }
83 }
84}
85
86#[derive(Debug, Clone, Default, Serialize, Deserialize)]
87pub struct ProfilingData {
88 pub functions: Vec<FunctionStats>,
89 pub cache: CacheStats,
90 #[serde(skip_serializing_if = "Option::is_none")]
91 pub span_tree: Option<SpanTree>,
92}
93
94#[derive(Debug, Clone, Default, Serialize, Deserialize)]
95pub struct ServerStartupStats {
96 pub server_name: String,
97 pub workspace_root: String,
98 pub init_time_ms: u64,
99 pub ready_time_ms: u64,
100 pub total_time_ms: u64,
101 pub functions: Vec<FunctionStats>,
102}
103
104#[derive(Debug, Clone, Default, Serialize, Deserialize)]
105pub struct ServerIndexingStats {
106 pub server_name: String,
107 pub file_count: u32,
108 pub total_time_ms: u64,
109 pub functions: Vec<FunctionStats>,
110 pub cache: CacheStats,
111}
112
113#[derive(Debug, Clone, Default, Serialize, Deserialize)]
114pub struct WorkspaceProfilingData {
115 pub workspace_root: String,
116 pub total_files: u32,
117 pub total_time_ms: u64,
118 pub server_profiles: Vec<ServerProfilingData>,
119}
120
121#[derive(Debug, Clone, Default, Serialize, Deserialize)]
122pub struct ServerProfilingData {
123 pub server_name: String,
124 pub startup: Option<ServerStartupStats>,
125 pub indexing: Option<ServerIndexingStats>,
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct RpcRequest<P> {
134 pub method: String,
135 pub params: P,
136 #[serde(default)]
137 pub profile: bool,
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct RpcSuccessResponse<R> {
142 pub result: R,
143 #[serde(skip_serializing_if = "Option::is_none")]
144 pub profiling: Option<ProfilingData>,
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize)]
148#[serde(untagged)]
149pub enum RpcResponse<R> {
150 Success(RpcSuccessResponse<R>),
151 Error { error: String },
152}
153
154impl<R> RpcResponse<R> {
155 pub fn success(result: R) -> Self {
156 RpcResponse::Success(RpcSuccessResponse {
157 result,
158 profiling: None,
159 })
160 }
161
162 pub fn success_with_profiling(result: R, profiling: ProfilingData) -> Self {
163 let profiling = if profiling.functions.is_empty() {
164 None
165 } else {
166 Some(profiling)
167 };
168 RpcResponse::Success(RpcSuccessResponse { result, profiling })
169 }
170
171 pub fn error(message: impl Into<String>) -> Self {
172 RpcResponse::Error {
173 error: message.into(),
174 }
175 }
176
177 pub fn into_result(self) -> Result<R, String> {
178 match self {
179 RpcResponse::Success(s) => Ok(s.result),
180 RpcResponse::Error { error } => Err(error),
181 }
182 }
183}
184
185#[derive(Debug, Clone, Default, Serialize, Deserialize)]
190pub struct ShutdownParams {}
191
192#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct ShutdownResult {
194 pub status: String,
195}
196
197#[derive(Debug, Clone, Default, Serialize, Deserialize)]
202pub struct DescribeSessionParams {
203 #[serde(default)]
204 pub include_profiling: bool,
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize)]
208pub struct DescribeSessionResult {
209 pub daemon_pid: u32,
210 pub caches: HashMap<String, CacheInfo>,
211 pub workspaces: Vec<WorkspaceInfo>,
212 #[serde(skip_serializing_if = "Option::is_none")]
213 pub profiling: Option<Vec<WorkspaceProfilingData>>,
214}
215
216#[derive(Debug, Clone, Serialize, Deserialize)]
221pub struct GrepParams {
222 pub workspace_root: String,
223 #[serde(default = "default_pattern")]
224 pub pattern: String,
225 #[serde(skip_serializing_if = "Option::is_none")]
226 pub kinds: Option<Vec<String>>,
227 #[serde(default)]
228 pub case_sensitive: bool,
229 #[serde(default)]
230 pub include_docs: bool,
231 #[serde(skip_serializing_if = "Option::is_none")]
232 pub path_pattern: Option<String>,
233 #[serde(default)]
234 pub exclude_patterns: Vec<String>,
235 #[serde(default = "default_head_limit")]
236 pub limit: u32,
237}
238
239fn default_head_limit() -> u32 {
240 DEFAULT_HEAD_LIMIT
241}
242
243fn default_pattern() -> String {
244 ".*".to_string()
245}
246
247#[derive(Debug, Clone, Serialize, Deserialize)]
248pub struct GrepResult {
249 #[serde(default)]
250 pub symbols: Vec<SymbolInfo>,
251 #[serde(skip_serializing_if = "Option::is_none")]
252 pub warning: Option<String>,
253 #[serde(default)]
254 pub truncated: bool,
255 #[serde(skip_serializing_if = "Option::is_none")]
256 pub total_count: Option<u32>,
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize)]
264pub struct FilesParams {
265 pub workspace_root: String,
266 #[serde(skip_serializing_if = "Option::is_none")]
267 pub subpath: Option<String>,
268 #[serde(default)]
269 pub exclude_patterns: Vec<String>,
270 #[serde(default)]
271 pub include_patterns: Vec<String>,
272 #[serde(skip_serializing_if = "Option::is_none")]
273 pub filter_pattern: Option<String>,
274 #[serde(default = "default_head_limit")]
275 pub head: u32,
276}
277
278#[derive(Debug, Clone, Serialize, Deserialize)]
279pub struct FilesResult {
280 pub files: HashMap<String, FileInfo>,
281 pub total_files: u32,
282 pub total_bytes: u64,
283 pub total_lines: u32,
284 #[serde(default)]
285 pub excluded_dirs: Vec<String>,
286 #[serde(default)]
287 pub truncated: bool,
288}
289
290#[derive(Debug, Clone, Serialize, Deserialize)]
295pub struct ShowParams {
296 pub workspace_root: String,
297 pub path: String,
298 pub line: u32,
299 #[serde(default)]
300 pub column: u32,
301 #[serde(default)]
302 pub context: u32,
303 #[serde(skip_serializing_if = "Option::is_none")]
304 pub head: Option<u32>,
305 #[serde(skip_serializing_if = "Option::is_none", alias = "symbol")]
306 pub symbol_name: Option<String>,
307 #[serde(skip_serializing_if = "Option::is_none", alias = "kind")]
308 pub symbol_kind: Option<String>,
309 #[serde(skip_serializing_if = "Option::is_none")]
310 pub range_start_line: Option<u32>,
311 #[serde(skip_serializing_if = "Option::is_none")]
312 pub range_end_line: Option<u32>,
313 #[serde(default)]
314 pub direct_location: bool,
315}
316
317#[derive(Debug, Clone, Serialize, Deserialize)]
318pub struct ShowResult {
319 pub path: String,
320 pub start_line: u32,
321 pub end_line: u32,
322 pub content: String,
323 #[serde(skip_serializing_if = "Option::is_none")]
324 pub symbol: Option<String>,
325 #[serde(default)]
326 pub truncated: bool,
327 #[serde(skip_serializing_if = "Option::is_none")]
328 pub total_lines: Option<u32>,
329}
330
331#[derive(Debug, Clone, Serialize, Deserialize)]
336pub struct ReferencesParams {
337 pub workspace_root: String,
338 pub path: String,
339 pub line: u32,
340 #[serde(default)]
341 pub column: u32,
342 #[serde(default)]
343 pub context: u32,
344 #[serde(default = "default_head_limit")]
345 pub head: u32,
346}
347
348#[derive(Debug, Clone, Serialize, Deserialize)]
349pub struct ReferencesResult {
350 pub locations: Vec<LocationInfo>,
351 #[serde(default)]
352 pub truncated: bool,
353 #[serde(skip_serializing_if = "Option::is_none")]
354 pub total_count: Option<u32>,
355}
356
357#[derive(Debug, Clone, Serialize, Deserialize)]
362pub struct DeclarationParams {
363 pub workspace_root: String,
364 pub path: String,
365 pub line: u32,
366 #[serde(default)]
367 pub column: u32,
368 #[serde(default)]
369 pub context: u32,
370 #[serde(default = "default_head_limit")]
371 pub head: u32,
372}
373
374#[derive(Debug, Clone, Serialize, Deserialize)]
375pub struct DeclarationResult {
376 pub locations: Vec<LocationInfo>,
377 #[serde(default)]
378 pub truncated: bool,
379 #[serde(skip_serializing_if = "Option::is_none")]
380 pub total_count: Option<u32>,
381}
382
383#[derive(Debug, Clone, Serialize, Deserialize)]
388pub struct ImplementationsParams {
389 pub workspace_root: String,
390 pub path: String,
391 pub line: u32,
392 #[serde(default)]
393 pub column: u32,
394 #[serde(default)]
395 pub context: u32,
396 #[serde(default = "default_head_limit")]
397 pub head: u32,
398}
399
400#[derive(Debug, Clone, Serialize, Deserialize)]
401pub struct ImplementationsResult {
402 #[serde(default)]
403 pub locations: Vec<LocationInfo>,
404 #[serde(skip_serializing_if = "Option::is_none")]
405 pub error: Option<String>,
406 #[serde(default)]
407 pub truncated: bool,
408 #[serde(skip_serializing_if = "Option::is_none")]
409 pub total_count: Option<u32>,
410}
411
412#[derive(Debug, Clone, Serialize, Deserialize)]
417pub struct SubtypesParams {
418 pub workspace_root: String,
419 pub path: String,
420 pub line: u32,
421 #[serde(default)]
422 pub column: u32,
423 #[serde(default)]
424 pub context: u32,
425 #[serde(default = "default_head_limit")]
426 pub head: u32,
427}
428
429#[derive(Debug, Clone, Serialize, Deserialize)]
430pub struct SubtypesResult {
431 pub locations: Vec<LocationInfo>,
432 #[serde(default)]
433 pub truncated: bool,
434 #[serde(skip_serializing_if = "Option::is_none")]
435 pub total_count: Option<u32>,
436}
437
438#[derive(Debug, Clone, Serialize, Deserialize)]
443pub struct SupertypesParams {
444 pub workspace_root: String,
445 pub path: String,
446 pub line: u32,
447 #[serde(default)]
448 pub column: u32,
449 #[serde(default)]
450 pub context: u32,
451 #[serde(default = "default_head_limit")]
452 pub head: u32,
453}
454
455#[derive(Debug, Clone, Serialize, Deserialize)]
456pub struct SupertypesResult {
457 pub locations: Vec<LocationInfo>,
458 #[serde(default)]
459 pub truncated: bool,
460 #[serde(skip_serializing_if = "Option::is_none")]
461 pub total_count: Option<u32>,
462}
463
464#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
469#[serde(rename_all = "lowercase")]
470pub enum CallsMode {
471 Outgoing,
472 Incoming,
473 Path,
474}
475
476#[derive(Debug, Clone, Serialize, Deserialize)]
477pub struct CallsParams {
478 pub workspace_root: String,
479 pub mode: CallsMode,
480 #[serde(skip_serializing_if = "Option::is_none")]
481 pub from_path: Option<String>,
482 #[serde(skip_serializing_if = "Option::is_none")]
483 pub from_line: Option<u32>,
484 #[serde(skip_serializing_if = "Option::is_none")]
485 pub from_column: Option<u32>,
486 #[serde(skip_serializing_if = "Option::is_none")]
487 pub from_symbol: Option<String>,
488 #[serde(skip_serializing_if = "Option::is_none")]
489 pub to_path: Option<String>,
490 #[serde(skip_serializing_if = "Option::is_none")]
491 pub to_line: Option<u32>,
492 #[serde(skip_serializing_if = "Option::is_none")]
493 pub to_column: Option<u32>,
494 #[serde(skip_serializing_if = "Option::is_none")]
495 pub to_symbol: Option<String>,
496 #[serde(default = "default_max_depth")]
497 pub max_depth: u32,
498 #[serde(default)]
499 pub include_non_workspace: bool,
500 #[serde(default = "default_head_limit")]
501 pub head: u32,
502}
503
504fn default_max_depth() -> u32 {
505 3
506}
507
508#[derive(Debug, Clone, Serialize, Deserialize)]
509pub struct CallsResult {
510 #[serde(skip_serializing_if = "Option::is_none")]
511 pub root: Option<CallNode>,
512 #[serde(skip_serializing_if = "Option::is_none")]
513 pub path: Option<Vec<CallNode>>,
514 #[serde(skip_serializing_if = "Option::is_none")]
515 pub message: Option<String>,
516 #[serde(skip_serializing_if = "Option::is_none")]
517 pub error: Option<String>,
518 #[serde(default)]
519 pub truncated: bool,
520}
521
522#[derive(Debug, Clone, Serialize, Deserialize)]
527pub struct RenameParams {
528 pub workspace_root: String,
529 pub path: String,
530 pub line: u32,
531 #[serde(default)]
532 pub column: u32,
533 pub new_name: String,
534}
535
536#[derive(Debug, Clone, Serialize, Deserialize)]
537pub struct RenameResult {
538 pub files_changed: Vec<String>,
539}
540
541#[derive(Debug, Clone, Serialize, Deserialize)]
546pub struct MoveFileParams {
547 pub workspace_root: String,
548 pub old_path: String,
549 pub new_path: String,
550}
551
552#[derive(Debug, Clone, Serialize, Deserialize)]
553pub struct MoveFileResult {
554 pub files_changed: Vec<String>,
555 pub imports_updated: bool,
556}
557
558#[derive(Debug, Clone, Serialize, Deserialize)]
563pub struct RawLspRequestParams {
564 pub workspace_root: String,
565 pub method: String,
566 #[serde(default)]
567 pub params: serde_json::Value,
568 #[serde(default = "default_language")]
569 pub language: String,
570}
571
572fn default_language() -> String {
573 "python".to_string()
574}
575
576#[derive(Debug, Clone, Serialize, Deserialize)]
581pub struct RestartWorkspaceParams {
582 pub workspace_root: String,
583}
584
585#[derive(Debug, Clone, Serialize, Deserialize)]
586pub struct RestartWorkspaceResult {
587 pub restarted: Vec<String>,
588}
589
590#[derive(Debug, Clone, Serialize, Deserialize)]
591pub struct RemoveWorkspaceParams {
592 pub workspace_root: String,
593}
594
595#[derive(Debug, Clone, Serialize, Deserialize)]
596pub struct RemoveWorkspaceResult {
597 pub servers_stopped: Vec<String>,
598}
599
600#[derive(Debug, Clone, Serialize, Deserialize)]
605pub struct AddWorkspaceParams {
606 pub workspace_root: String,
607}
608
609#[derive(Debug, Clone, Serialize, Deserialize)]
610pub struct AddWorkspaceResult {
611 pub added: bool,
612 pub workspace_root: String,
613 pub message: String,
614}
615
616#[derive(Debug, Clone, Serialize, Deserialize)]
621pub struct ResolveSymbolParams {
622 pub workspace_root: String,
623 pub symbol_path: String,
624}
625
626#[derive(Debug, Clone, Serialize, Deserialize)]
627pub struct ResolveSymbolResult {
628 #[serde(skip_serializing_if = "Option::is_none")]
629 pub path: Option<String>,
630 #[serde(skip_serializing_if = "Option::is_none")]
631 pub line: Option<u32>,
632 #[serde(skip_serializing_if = "Option::is_none")]
633 pub column: Option<u32>,
634 #[serde(skip_serializing_if = "Option::is_none")]
635 pub name: Option<String>,
636 #[serde(skip_serializing_if = "Option::is_none")]
637 pub kind: Option<String>,
638 #[serde(skip_serializing_if = "Option::is_none")]
639 pub container: Option<String>,
640 #[serde(skip_serializing_if = "Option::is_none")]
641 pub range_start_line: Option<u32>,
642 #[serde(skip_serializing_if = "Option::is_none")]
643 pub range_end_line: Option<u32>,
644 #[serde(skip_serializing_if = "Option::is_none")]
645 pub error: Option<String>,
646 #[serde(skip_serializing_if = "Option::is_none")]
647 pub matches: Option<Vec<SymbolInfo>>,
648 #[serde(skip_serializing_if = "Option::is_none")]
649 pub total_matches: Option<u32>,
650}
651
652#[derive(Default)]
653pub struct ResolveSymbolResultBuilder {
654 pub path: String,
655 pub line: u32,
656 pub column: u32,
657 pub name: Option<String>,
658 pub kind: Option<String>,
659 pub container: Option<String>,
660 pub range_start_line: Option<u32>,
661 pub range_end_line: Option<u32>,
662}
663
664impl ResolveSymbolResult {
665 pub fn success(builder: ResolveSymbolResultBuilder) -> Self {
666 Self {
667 path: Some(builder.path),
668 line: Some(builder.line),
669 column: Some(builder.column),
670 name: builder.name,
671 kind: builder.kind,
672 container: builder.container,
673 range_start_line: builder.range_start_line,
674 range_end_line: builder.range_end_line,
675 error: None,
676 matches: None,
677 total_matches: None,
678 }
679 }
680
681 pub fn not_found(symbol: &str) -> Self {
682 Self {
683 error: Some(format!("Symbol '{}' not found", symbol)),
684 path: None,
685 line: None,
686 column: None,
687 name: None,
688 kind: None,
689 container: None,
690 range_start_line: None,
691 range_end_line: None,
692 matches: None,
693 total_matches: None,
694 }
695 }
696
697 pub fn ambiguous(symbol: &str, matches: Vec<SymbolInfo>, total: u32) -> Self {
698 Self {
699 error: Some(format!(
700 "Symbol '{}' is ambiguous ({} matches)",
701 symbol, total
702 )),
703 matches: Some(matches),
704 total_matches: Some(total),
705 path: None,
706 line: None,
707 column: None,
708 name: None,
709 kind: None,
710 container: None,
711 range_start_line: None,
712 range_end_line: None,
713 }
714 }
715}