Skip to main content

rust_analyzer_cli/lsp/
types.rs

1use serde::{Deserialize, Serialize};
2use std::path::PathBuf;
3
4#[derive(Debug, Serialize, Deserialize)]
5pub struct DaemonStatusResponse {
6    pub ready: bool,
7    pub indexed_at: Option<String>,
8    pub workspace_root: String,
9    pub process_id: u32,
10}
11
12#[derive(Debug, Serialize, Deserialize)]
13pub struct SymbolQueryRequest {
14    pub name: String,
15    pub kind: String,
16    pub exact: bool,
17}
18
19#[derive(Debug, Serialize, Deserialize)]
20pub struct SymbolItem {
21    pub name: String,
22    pub kind: String,
23    pub file: String,
24    pub line: u32,
25    pub col: u32,
26    pub container_name: Option<String>,
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub body: Option<String>,
29}
30
31#[derive(Debug, Serialize, Deserialize)]
32pub struct OutlineQueryRequest {
33    pub file: PathBuf,
34}
35
36#[derive(Debug, Serialize, Deserialize)]
37pub struct OutlineItem {
38    pub name: String,
39    pub kind: String,
40    pub detail: Option<String>,
41    pub line: u32,
42    pub col: u32,
43    pub end_line: u32,
44    pub children: Vec<OutlineItem>,
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub body: Option<String>,
47}
48
49#[derive(Debug, Serialize, Deserialize)]
50pub struct DefinitionQueryRequest {
51    pub file: PathBuf,
52    pub line: u32,
53    pub col: u32,
54}
55
56#[derive(Debug, Serialize, Deserialize)]
57pub struct BodyQueryRequest {
58    pub file: PathBuf,
59    pub line: u32,
60    pub col: u32,
61    #[serde(default = "default_body_max_lines")]
62    pub max_lines: usize,
63}
64
65fn default_body_max_lines() -> usize {
66    100
67}
68
69#[derive(Debug, Serialize, Deserialize)]
70pub struct DefinitionItem {
71    pub file: String,
72    pub line: u32,
73    pub col: u32,
74    pub end_line: u32,
75    pub end_col: u32,
76    pub snippet: Option<String>,
77    #[serde(skip_serializing_if = "Option::is_none")]
78    pub body: Option<String>,
79}
80
81#[derive(Debug, Serialize, Deserialize)]
82pub struct CursorQueryRequest {
83    pub file: PathBuf,
84    pub line: u32,
85    pub col: u32,
86    pub mode: String, // "incoming", "outgoing", "references"
87    pub depth: u32,
88}
89
90#[derive(Debug, Serialize, Deserialize)]
91pub struct CursorItem {
92    pub name: String,
93    pub kind: String,
94    pub file: String,
95    pub line: u32,
96    pub col: u32,
97    pub caller_or_callee: Option<String>,
98}
99
100#[derive(Debug, Serialize, Deserialize)]
101pub struct TypeHierarchyQueryRequest {
102    pub file: PathBuf,
103    pub line: u32,
104    pub col: u32,
105    pub mode: String, // "supertypes", "subtypes"
106    pub depth: u32,
107}
108
109#[derive(Debug, Serialize, Deserialize)]
110pub struct TypeHierarchyItemResult {
111    pub name: String,
112    pub kind: String,
113    pub file: String,
114    pub line: u32,
115    pub col: u32,
116    pub detail: Option<String>,
117}
118
119#[derive(Debug, Serialize, Deserialize)]
120pub struct BodyItem {
121    pub file: String,
122    pub line: u32,
123    pub col: u32,
124    pub end_line: u32,
125    pub end_col: u32,
126    pub body: String,
127    pub total_lines: usize,
128    pub is_truncated: bool,
129}
130
131#[derive(Debug, Serialize, Deserialize)]
132pub struct CheckQueryRequest {
133    pub target: Option<String>,
134}
135
136#[derive(Debug, Serialize, Deserialize)]
137pub struct CheckDiagnosticItem {
138    pub message: String,
139    pub level: String, // "error", "warning"
140    pub file: Option<String>,
141    pub line: Option<u32>,
142    pub col: Option<u32>,
143}
144
145#[derive(Debug, Serialize, Deserialize)]
146pub struct CheckResponse {
147    pub success: bool,
148    pub diagnostics: Vec<CheckDiagnosticItem>,
149}
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154
155    #[test]
156    fn test_daemon_status_json_roundtrip() {
157        let status = DaemonStatusResponse {
158            ready: true,
159            indexed_at: Some("2026-07-31T10:00:00Z".to_string()),
160            workspace_root: "/path/to/workspace".to_string(),
161            process_id: 12345,
162        };
163        let json = serde_json::to_string(&status).unwrap();
164        let decoded: DaemonStatusResponse = serde_json::from_str(&json).unwrap();
165        assert!(decoded.ready);
166        assert_eq!(decoded.process_id, 12345);
167        assert_eq!(decoded.workspace_root, "/path/to/workspace");
168    }
169
170    #[test]
171    fn test_symbol_item_json_roundtrip() {
172        let item = SymbolItem {
173            name: "ExampleStruct".to_string(),
174            kind: "struct".to_string(),
175            file: "tests/code_example.rs".to_string(),
176            line: 15,
177            col: 1,
178            container_name: Some("lsp".to_string()),
179            body: None,
180        };
181
182        let json = serde_json::to_string(&item).unwrap();
183        let decoded: SymbolItem = serde_json::from_str(&json).unwrap();
184        assert_eq!(decoded.name, "ExampleStruct");
185        assert_eq!(decoded.kind, "struct");
186    }
187
188    #[test]
189    fn test_body_query_request_defaults_to_100_lines() {
190        let request: BodyQueryRequest =
191            serde_json::from_str(r#"{"file":"tests/code_example.rs","line":15,"col":10}"#).unwrap();
192        assert_eq!(request.max_lines, 100);
193    }
194}