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    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/cursor", post(handle_cursor))
25        .route("/api/type-hierarchy", post(handle_type_hierarchy))
26        .route("/api/check", post(handle_check))
27        .with_state(state);
28
29    let addr = SocketAddr::from(([127, 0, 0, 1], port));
30    info!("rust-analyzer-cli daemon listening on http://{}", addr);
31    println!("rust-analyzer-cli daemon started on http://{}", addr);
32
33    let listener = tokio::net::TcpListener::bind(addr).await?;
34    axum::serve(listener, app).await?;
35    Ok(())
36}
37
38async fn handle_status(
39    State(state): State<Arc<AppState>>,
40) -> Result<Json<DaemonStatusResponse>, (StatusCode, String)> {
41    let client_opt = state.lsp_client.read().await;
42    let (ready, pid) = match client_opt.as_ref() {
43        Some(c) => (true, c.process_id),
44        None => (false, 0),
45    };
46
47    Ok(Json(DaemonStatusResponse {
48        ready,
49        indexed_at: Some(chrono_now_str()),
50        workspace_root: state.workspace_root.to_string_lossy().to_string(),
51        process_id: pid,
52    }))
53}
54
55async fn handle_refresh(
56    State(state): State<Arc<AppState>>,
57) -> Result<Json<serde_json::Value>, (StatusCode, String)> {
58    state
59        .refresh_lsp()
60        .await
61        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
62    Ok(Json(serde_json::json!({ "success": true, "message": "LSP session refreshed" })))
63}
64
65async fn handle_symbol(
66    State(state): State<Arc<AppState>>,
67    Json(req): Json<SymbolQueryRequest>,
68) -> Result<Json<Vec<SymbolItem>>, (StatusCode, String)> {
69    let client = state
70        .get_or_start_lsp()
71        .await
72        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
73
74    let items = client
75        .query_symbol(&req.name, &req.kind, req.exact)
76        .await
77        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
78
79    Ok(Json(items))
80}
81
82async fn handle_outline(
83    State(state): State<Arc<AppState>>,
84    Json(req): Json<OutlineQueryRequest>,
85) -> Result<Json<Vec<OutlineItem>>, (StatusCode, String)> {
86    let client = state
87        .get_or_start_lsp()
88        .await
89        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
90
91    let items = client
92        .query_outline(&req.file)
93        .await
94        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
95
96    Ok(Json(items))
97}
98
99async fn handle_definition(
100    State(state): State<Arc<AppState>>,
101    Json(req): Json<DefinitionQueryRequest>,
102) -> Result<Json<Vec<DefinitionItem>>, (StatusCode, String)> {
103    let client = state
104        .get_or_start_lsp()
105        .await
106        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
107
108    let items = client
109        .query_definition(&req.file, req.line, req.col)
110        .await
111        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
112
113    Ok(Json(items))
114}
115
116async fn handle_cursor(
117    State(state): State<Arc<AppState>>,
118    Json(req): Json<CursorQueryRequest>,
119) -> Result<Json<Vec<CursorItem>>, (StatusCode, String)> {
120    let client = state
121        .get_or_start_lsp()
122        .await
123        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
124
125    let items = client
126        .query_cursor(&req.file, req.line, req.col, &req.mode, req.depth)
127        .await
128        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
129
130    Ok(Json(items))
131}
132
133async fn handle_type_hierarchy(
134    State(state): State<Arc<AppState>>,
135    Json(req): Json<TypeHierarchyQueryRequest>,
136) -> Result<Json<Vec<TypeHierarchyItemResult>>, (StatusCode, String)> {
137    let client = state
138        .get_or_start_lsp()
139        .await
140        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
141
142    let items = client
143        .query_type_hierarchy(&req.file, req.line, req.col, &req.mode, req.depth)
144        .await
145        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
146
147    Ok(Json(items))
148}
149
150
151async fn handle_check(
152    State(state): State<Arc<AppState>>,
153    Json(req): Json<CheckQueryRequest>,
154) -> Result<Json<CheckResponse>, (StatusCode, String)> {
155    let res = run_cargo_check(&state.workspace_root, req.target.as_deref())
156        .await
157        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
158
159    Ok(Json(res))
160}
161
162fn chrono_now_str() -> String {
163    let now = std::time::SystemTime::now();
164    format!("{:?}", now)
165}