Skip to main content

rust_analyzer_cli/daemon/
server.rs

1use crate::cargo::run_cargo_check;
2use crate::daemon::state::AppState;
3use crate::lsp::types::*;
4use axum::{
5    Json, Router,
6    extract::State,
7    http::StatusCode,
8    routing::{get, post},
9};
10use std::net::SocketAddr;
11use std::path::{Path, PathBuf};
12use std::sync::Arc;
13use tracing::info;
14
15pub async fn start_daemon_server(workspace_root: PathBuf, port: u16) -> anyhow::Result<()> {
16    start_daemon_server_with_output(workspace_root, port, false).await
17}
18
19pub async fn start_daemon_server_with_output(
20    workspace_root: PathBuf,
21    port: u16,
22    json_output: bool,
23) -> anyhow::Result<()> {
24    let state = Arc::new(AppState::new(workspace_root));
25
26    let app = Router::new()
27        .route("/status", get(handle_status))
28        .route("/refresh", post(handle_refresh))
29        .route("/api/symbol", post(handle_symbol))
30        .route("/api/outline", post(handle_outline))
31        .route("/api/definition", post(handle_definition))
32        .route("/api/body", post(handle_body))
33        .route("/api/hover", post(handle_hover))
34        .route("/api/cursor", post(handle_cursor))
35        .route("/api/type-hierarchy", post(handle_type_hierarchy))
36        .route("/api/check", post(handle_check))
37        .with_state(state.clone());
38
39    let addr = SocketAddr::from(([127, 0, 0, 1], port));
40    let listener = tokio::net::TcpListener::bind(addr).await?;
41    let actual_addr = listener.local_addr()?;
42    info!(
43        "rust-analyzer-cli daemon listening on http://{}",
44        actual_addr
45    );
46    if json_output {
47        println!(
48            "{}",
49            daemon_started_payload(actual_addr, &state.workspace_root)
50        );
51    } else {
52        println!(
53            "rust-analyzer-cli daemon started on http://{}\nWorkspace: {}\nKeep this process running while issuing query commands.",
54            actual_addr,
55            state.workspace_root.display()
56        );
57    }
58
59    axum::serve(listener, app).await?;
60    Ok(())
61}
62
63fn daemon_started_payload(address: SocketAddr, workspace_root: &Path) -> serde_json::Value {
64    serde_json::json!({
65        "success": true,
66        "message": "rust-analyzer-cli daemon started",
67        "address": format!("http://{}", address),
68        "workspace": workspace_root.to_string_lossy(),
69    })
70}
71
72async fn handle_status(
73    State(state): State<Arc<AppState>>,
74) -> Result<Json<DaemonStatusResponse>, (StatusCode, String)> {
75    let client_opt = state.lsp_client.read().await;
76    let (ready, pid) = match client_opt.as_ref() {
77        Some(c) => (true, c.process_id),
78        None => (false, 0),
79    };
80
81    Ok(Json(DaemonStatusResponse {
82        ready,
83        indexed_at: Some(chrono_now_str()),
84        workspace_root: state.workspace_root.to_string_lossy().to_string(),
85        process_id: pid,
86    }))
87}
88
89async fn handle_refresh(
90    State(state): State<Arc<AppState>>,
91) -> Result<Json<serde_json::Value>, (StatusCode, String)> {
92    state
93        .refresh_lsp()
94        .await
95        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
96    Ok(Json(
97        serde_json::json!({ "success": true, "message": "LSP session refreshed" }),
98    ))
99}
100
101async fn handle_symbol(
102    State(state): State<Arc<AppState>>,
103    Json(req): Json<SymbolQueryRequest>,
104) -> Result<Json<Vec<SymbolItem>>, (StatusCode, String)> {
105    let client = state
106        .get_or_start_lsp()
107        .await
108        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
109
110    let items = client
111        .query_symbol(&req.name, &req.kind, req.exact, false, 100)
112        .await
113        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
114
115    Ok(Json(items))
116}
117
118async fn handle_outline(
119    State(state): State<Arc<AppState>>,
120    Json(req): Json<OutlineQueryRequest>,
121) -> Result<Json<Vec<OutlineItem>>, (StatusCode, String)> {
122    let client = state
123        .get_or_start_lsp()
124        .await
125        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
126
127    let items = client
128        .query_outline(&req.file, false, 100)
129        .await
130        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
131
132    Ok(Json(items))
133}
134
135async fn handle_definition(
136    State(state): State<Arc<AppState>>,
137    Json(req): Json<DefinitionQueryRequest>,
138) -> Result<Json<Vec<DefinitionItem>>, (StatusCode, String)> {
139    let client = state
140        .get_or_start_lsp()
141        .await
142        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
143
144    let items = client
145        .query_definition(&req.file, req.line, req.col, false, 100)
146        .await
147        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
148
149    Ok(Json(items))
150}
151
152async fn handle_body(
153    State(state): State<Arc<AppState>>,
154    Json(req): Json<BodyQueryRequest>,
155) -> Result<Json<BodyItem>, (StatusCode, String)> {
156    let client = state
157        .get_or_start_lsp()
158        .await
159        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
160
161    let item = client
162        .query_body(&req.file, req.line, req.col, req.max_lines)
163        .await
164        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
165
166    Ok(Json(item))
167}
168
169async fn handle_hover(
170    State(state): State<Arc<AppState>>,
171    Json(req): Json<HoverQueryRequest>,
172) -> Result<Json<Option<HoverItem>>, (StatusCode, String)> {
173    let client = state
174        .get_or_start_lsp()
175        .await
176        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
177
178    let item = client
179        .query_hover(&req.file, req.line, req.col)
180        .await
181        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
182
183    Ok(Json(item))
184}
185
186async fn handle_cursor(
187    State(state): State<Arc<AppState>>,
188    Json(req): Json<CursorQueryRequest>,
189) -> Result<Json<Vec<CursorItem>>, (StatusCode, String)> {
190    let client = state
191        .get_or_start_lsp()
192        .await
193        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
194
195    let items = client
196        .query_cursor(&req.file, req.line, req.col, &req.mode, req.depth)
197        .await
198        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
199
200    Ok(Json(items))
201}
202
203async fn handle_type_hierarchy(
204    State(state): State<Arc<AppState>>,
205    Json(req): Json<TypeHierarchyQueryRequest>,
206) -> Result<Json<Vec<TypeHierarchyItemResult>>, (StatusCode, String)> {
207    let client = state
208        .get_or_start_lsp()
209        .await
210        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
211
212    let items = client
213        .query_type_hierarchy(&req.file, req.line, req.col, &req.mode, req.depth)
214        .await
215        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
216
217    Ok(Json(items))
218}
219
220async fn handle_check(
221    State(state): State<Arc<AppState>>,
222    Json(req): Json<CheckQueryRequest>,
223) -> Result<Json<CheckResponse>, (StatusCode, String)> {
224    let res = run_cargo_check(&state.workspace_root, req.target.as_deref())
225        .await
226        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
227
228    Ok(Json(res))
229}
230
231fn chrono_now_str() -> String {
232    let now = std::time::SystemTime::now();
233    format!("{:?}", now)
234}
235
236#[cfg(test)]
237mod tests {
238    use super::*;
239
240    #[test]
241    fn test_daemon_started_payload_is_machine_readable() {
242        let payload = daemon_started_payload(
243            SocketAddr::from(([127, 0, 0, 1], 60094)),
244            Path::new("workspace"),
245        );
246        let encoded = serde_json::to_string(&payload).unwrap();
247        let decoded: serde_json::Value = serde_json::from_str(&encoded).unwrap();
248        assert_eq!(decoded["success"], true);
249        assert_eq!(decoded["address"], "http://127.0.0.1:60094");
250    }
251}