rust_analyzer_cli/daemon/
server.rs1use crate::cargo::run_cargo_check;
2use crate::daemon::state::AppState;
3use crate::lsp::types::*;
4use axum::{
5 extract::State,
6 http::StatusCode,
7 routing::{get, post},
8 Json, Router,
9};
10use std::net::SocketAddr;
11use std::path::PathBuf;
12use std::sync::Arc;
13use tracing::info;
14
15pub async fn start_daemon_server(workspace_root: PathBuf, port: u16) -> anyhow::Result<()> {
16 let state = Arc::new(AppState::new(workspace_root));
17
18 let app = Router::new()
19 .route("/status", get(handle_status))
20 .route("/refresh", post(handle_refresh))
21 .route("/api/symbol", post(handle_symbol))
22 .route("/api/outline", post(handle_outline))
23 .route("/api/definition", post(handle_definition))
24 .route("/api/body", post(handle_body))
25 .route("/api/cursor", post(handle_cursor))
26 .route("/api/type-hierarchy", post(handle_type_hierarchy))
27 .route("/api/check", post(handle_check))
28 .with_state(state);
29
30 let addr = SocketAddr::from(([127, 0, 0, 1], port));
31 info!("rust-analyzer-cli daemon listening on http://{}", addr);
32 println!("rust-analyzer-cli daemon started on http://{}", addr);
33
34 let listener = tokio::net::TcpListener::bind(addr).await?;
35 axum::serve(listener, app).await?;
36 Ok(())
37}
38
39async fn handle_status(
40 State(state): State<Arc<AppState>>,
41) -> Result<Json<DaemonStatusResponse>, (StatusCode, String)> {
42 let client_opt = state.lsp_client.read().await;
43 let (ready, pid) = match client_opt.as_ref() {
44 Some(c) => (true, c.process_id),
45 None => (false, 0),
46 };
47
48 Ok(Json(DaemonStatusResponse {
49 ready,
50 indexed_at: Some(chrono_now_str()),
51 workspace_root: state.workspace_root.to_string_lossy().to_string(),
52 process_id: pid,
53 }))
54}
55
56async fn handle_refresh(
57 State(state): State<Arc<AppState>>,
58) -> Result<Json<serde_json::Value>, (StatusCode, String)> {
59 state
60 .refresh_lsp()
61 .await
62 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
63 Ok(Json(serde_json::json!({ "success": true, "message": "LSP session refreshed" })))
64}
65
66async fn handle_symbol(
67 State(state): State<Arc<AppState>>,
68 Json(req): Json<SymbolQueryRequest>,
69) -> Result<Json<Vec<SymbolItem>>, (StatusCode, String)> {
70 let client = state
71 .get_or_start_lsp()
72 .await
73 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
74
75 let items = client
76 .query_symbol(&req.name, &req.kind, req.exact, false, 100)
77 .await
78 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
79
80 Ok(Json(items))
81}
82
83async fn handle_outline(
84 State(state): State<Arc<AppState>>,
85 Json(req): Json<OutlineQueryRequest>,
86) -> Result<Json<Vec<OutlineItem>>, (StatusCode, String)> {
87 let client = state
88 .get_or_start_lsp()
89 .await
90 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
91
92 let items = client
93 .query_outline(&req.file, false, 100)
94 .await
95 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
96
97 Ok(Json(items))
98}
99
100async fn handle_definition(
101 State(state): State<Arc<AppState>>,
102 Json(req): Json<DefinitionQueryRequest>,
103) -> Result<Json<Vec<DefinitionItem>>, (StatusCode, String)> {
104 let client = state
105 .get_or_start_lsp()
106 .await
107 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
108
109 let items = client
110 .query_definition(&req.file, req.line, req.col, false, 100)
111 .await
112 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
113
114 Ok(Json(items))
115}
116
117async fn handle_body(
118 State(state): State<Arc<AppState>>,
119 Json(req): Json<DefinitionQueryRequest>,
120) -> Result<Json<BodyItem>, (StatusCode, String)> {
121 let client = state
122 .get_or_start_lsp()
123 .await
124 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
125
126 let item = client
127 .query_body(&req.file, req.line, req.col, 100)
128 .await
129 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
130
131 Ok(Json(item))
132}
133
134
135async fn handle_cursor(
136 State(state): State<Arc<AppState>>,
137 Json(req): Json<CursorQueryRequest>,
138) -> Result<Json<Vec<CursorItem>>, (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_cursor(&req.file, req.line, req.col, &req.mode, req.depth)
146 .await
147 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
148
149 Ok(Json(items))
150}
151
152async fn handle_type_hierarchy(
153 State(state): State<Arc<AppState>>,
154 Json(req): Json<TypeHierarchyQueryRequest>,
155) -> Result<Json<Vec<TypeHierarchyItemResult>>, (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 items = client
162 .query_type_hierarchy(&req.file, req.line, req.col, &req.mode, req.depth)
163 .await
164 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
165
166 Ok(Json(items))
167}
168
169
170async fn handle_check(
171 State(state): State<Arc<AppState>>,
172 Json(req): Json<CheckQueryRequest>,
173) -> Result<Json<CheckResponse>, (StatusCode, String)> {
174 let res = run_cargo_check(&state.workspace_root, req.target.as_deref())
175 .await
176 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
177
178 Ok(Json(res))
179}
180
181fn chrono_now_str() -> String {
182 let now = std::time::SystemTime::now();
183 format!("{:?}", now)
184}