Skip to content
This repository was archived by the owner on Sep 23, 2025. It is now read-only.

Commit 82b378c

Browse files
committed
change over to well-typed results
1 parent 2d5ced4 commit 82b378c

File tree

4 files changed

+359
-271
lines changed

4 files changed

+359
-271
lines changed

server/src/ipc.rs

Lines changed: 62 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@
33
//! Handles Unix socket/named pipe communication with the VSCode extension.
44
//! Ports the logic from server/src/ipc.ts to Rust with cross-platform support.
55
6+
use crate::synthetic_pr::{FeedbackType, UserFeedback};
67
use crate::types::{
78
FindAllReferencesPayload, GetSelectionResult, GoodbyePayload, IPCMessage, IPCMessageType,
8-
LogLevel, LogParams, PoloPayload, PresentReviewParams, PresentReviewResult,
9-
ResolveSymbolByNamePayload, ResponsePayload, UserFeedbackPayload,
9+
LogLevel, LogParams, PoloPayload, PresentReviewParams, ResolveSymbolByNamePayload,
10+
ResponsePayload, UserFeedbackPayload,
1011
};
12+
use anyhow::Context;
1113
use futures::FutureExt;
14+
use serde::de::DeserializeOwned;
1215
use serde_json;
1316
use std::collections::HashMap;
1417
use std::future::Future;
@@ -139,13 +142,10 @@ impl IPCCommunicator {
139142
Ok(())
140143
}
141144

142-
pub async fn present_review(&self, params: PresentReviewParams) -> Result<PresentReviewResult> {
145+
pub async fn present_review(&self, params: PresentReviewParams) -> Result<()> {
143146
if self.test_mode {
144147
info!("Present review called (test mode): {:?}", params);
145-
return Ok(PresentReviewResult {
146-
success: true,
147-
message: Some("Review successfully displayed (test mode)".to_string()),
148-
});
148+
return Ok(());
149149
}
150150

151151
// Ensure connection is established before proceeding
@@ -169,24 +169,15 @@ impl IPCCommunicator {
169169
debug!("Sending present_review message: {:?}", message);
170170
trace!("About to call send_message_with_reply for present_review");
171171

172-
let response = self.send_message_with_reply(message).await?;
172+
let response: () = self.send_message_with_reply(message).await?;
173173

174174
trace!(
175175
"Received response from send_message_with_reply: {:?}",
176176
response
177177
);
178178

179179
// Convert response to PresentReviewResult
180-
Ok(PresentReviewResult {
181-
success: response.success,
182-
message: response.error.or_else(|| {
183-
if response.success {
184-
Some("Review successfully displayed".to_string())
185-
} else {
186-
Some("Unknown error".to_string())
187-
}
188-
}),
189-
})
180+
Ok(())
190181
}
191182

192183
pub async fn get_selection(&self) -> Result<GetSelectionResult> {
@@ -224,25 +215,8 @@ impl IPCCommunicator {
224215

225216
debug!("Sending get_selection message: {:?}", message);
226217

227-
let response = self.send_message_with_reply(message).await?;
228-
229-
if let Some(data) = response.data {
230-
let selection: GetSelectionResult = serde_json::from_value(data)?;
231-
Ok(selection)
232-
} else {
233-
Ok(GetSelectionResult {
234-
selected_text: None,
235-
file_path: None,
236-
start_line: None,
237-
start_column: None,
238-
end_line: None,
239-
end_column: None,
240-
line_number: None,
241-
document_language: None,
242-
is_untitled: None,
243-
message: Some("No selection data in response".to_string()),
244-
})
245-
}
218+
let selection: GetSelectionResult = self.send_message_with_reply(message).await?;
219+
Ok(selection)
246220
}
247221

248222
pub async fn send_log(&self, level: LogLevel, message: String) {
@@ -364,10 +338,19 @@ impl IPCCommunicator {
364338
pub async fn send_create_synthetic_pr(
365339
&self,
366340
review_response: &crate::synthetic_pr::ReviewData,
367-
) -> Result<()> {
341+
) -> Result<UserFeedback> {
368342
if self.test_mode {
369343
info!("Synthetic PR creation message sent (test mode)");
370-
return Ok(());
344+
return Ok(UserFeedback {
345+
review_id: None,
346+
feedback_type: FeedbackType::CompleteReview,
347+
file_path: None,
348+
line_number: None,
349+
comment_text: None,
350+
completion_action: None,
351+
additional_notes: None,
352+
context_lines: None,
353+
});
371354
}
372355

373356
let shell_pid = {
@@ -396,7 +379,7 @@ impl IPCCommunicator {
396379
"Sending create_synthetic_pr message for review: {}",
397380
review_response.review_id
398381
);
399-
self.send_message_without_reply(message).await
382+
self.send_message_with_reply(message).await
400383
}
401384

402385
/// Wait for user feedback on a specific review
@@ -461,7 +444,10 @@ impl IPCCommunicator {
461444

462445
/// Send review update to VSCode extension and wait for user response
463446
/// This method blocks until the user provides feedback via the VSCode extension
464-
pub async fn send_review_update<T: serde::Serialize>(&self, review: &T) -> Result<crate::synthetic_pr::UserFeedback> {
447+
pub async fn send_review_update<T: serde::Serialize>(
448+
&self,
449+
review: &T,
450+
) -> Result<crate::synthetic_pr::UserFeedback> {
465451
if self.test_mode {
466452
info!("Send review update called (test mode)");
467453
return Ok(crate::synthetic_pr::UserFeedback {
@@ -492,19 +478,9 @@ impl IPCCommunicator {
492478
};
493479

494480
// Send message and wait for response
495-
let response = self.send_message_with_reply(message).await?;
481+
let response: UserFeedback = self.send_message_with_reply(message).await?;
496482

497-
// Parse UserFeedback from response data
498-
if let Some(data) = response.data {
499-
let user_feedback: crate::synthetic_pr::UserFeedback = serde_json::from_value(data)
500-
.map_err(IPCError::SerializationError)?;
501-
Ok(user_feedback)
502-
} else {
503-
Err(IPCError::ConnectionFailed {
504-
path: "user_feedback".to_string(),
505-
source: std::io::Error::new(std::io::ErrorKind::InvalidData, "No user feedback data in response")
506-
})
507-
}
483+
Ok(response)
508484
}
509485

510486
/// Gracefully shutdown the IPC communicator, sending Goodbye discovery message
@@ -529,7 +505,10 @@ impl IPCCommunicator {
529505
/// Sets up response correlation using the message UUID and waits up to 5 seconds
530506
/// for the background reader task to deliver the matching response.
531507
/// Uses the underlying `write_message` primitive to send the data.
532-
async fn send_message_with_reply(&self, message: IPCMessage) -> Result<ResponsePayload> {
508+
async fn send_message_with_reply<R>(&self, message: IPCMessage) -> Result<R>
509+
where
510+
R: DeserializeOwned,
511+
{
533512
debug!(
534513
"Sending IPC message with ID: {} (PID: {})",
535514
message.id,
@@ -572,7 +551,20 @@ impl IPCCommunicator {
572551
})?
573552
.map_err(|_| IPCError::ChannelClosed)?;
574553

575-
Ok(response)
554+
// Parse UserFeedback from response data
555+
if let Some(data) = response.data {
556+
let user_feedback: R =
557+
serde_json::from_value(data).map_err(IPCError::SerializationError)?;
558+
Ok(user_feedback)
559+
} else {
560+
Err(IPCError::ConnectionFailed {
561+
path: format!("{:?}", message.message_type),
562+
source: std::io::Error::new(
563+
std::io::ErrorKind::InvalidData,
564+
"No response data in response",
565+
),
566+
})
567+
}
576568
}
577569

578570
/// Sends an IPC message without waiting for a response (fire-and-forget)
@@ -962,23 +954,10 @@ impl crate::ide::IpcClient for IPCCommunicator {
962954
id: Uuid::new_v4().to_string(),
963955
};
964956

965-
let response = self.send_message_with_reply(message).await?;
966-
967-
if !response.success {
968-
return Err(anyhow::anyhow!(
969-
"VSCode extension failed to resolve symbol '{}': {}",
970-
name,
971-
response
972-
.error
973-
.unwrap_or_else(|| "Unknown error".to_string())
974-
));
975-
}
976-
977-
// Parse the response data as Vec<ResolvedSymbol>
978-
let symbols: Vec<crate::ide::SymbolDef> = match response.data {
979-
Some(data) => serde_json::from_value(data)?,
980-
None => vec![],
981-
};
957+
let symbols: Vec<crate::ide::SymbolDef> = self
958+
.send_message_with_reply(message)
959+
.await
960+
.with_context(|| format!("failed to resolve symbol '{name}'"))?;
982961

983962
Ok(symbols)
984963
}
@@ -1008,23 +987,15 @@ impl crate::ide::IpcClient for IPCCommunicator {
1008987
id: Uuid::new_v4().to_string(),
1009988
};
1010989

1011-
let response = self.send_message_with_reply(message).await?;
1012-
1013-
if !response.success {
1014-
return Err(anyhow::anyhow!(
1015-
"VSCode extension failed to find references for symbol '{}': {}",
1016-
symbol.name,
1017-
response
1018-
.error
1019-
.unwrap_or_else(|| "Unknown error".to_string())
1020-
));
1021-
}
1022-
1023-
// Parse the response data as Vec<FileLocation>
1024-
let locations: Vec<crate::ide::FileRange> = match response.data {
1025-
Some(data) => serde_json::from_value(data)?,
1026-
None => vec![],
1027-
};
990+
let locations: Vec<crate::ide::FileRange> = self
991+
.send_message_with_reply(message)
992+
.await
993+
.with_context(|| {
994+
format!(
995+
"VSCode extension failed to find references for symbol '{}'",
996+
symbol.name
997+
)
998+
})?;
1028999

10291000
Ok(locations)
10301001
}
@@ -1059,10 +1030,7 @@ mod test {
10591030
let result = ipc.present_review(params).await;
10601031
assert!(result.is_ok());
10611032

1062-
let review_result = result.unwrap();
1063-
assert!(review_result.success);
1064-
assert!(review_result.message.is_some());
1065-
assert!(review_result.message.unwrap().contains("test mode"));
1033+
result.unwrap();
10661034
}
10671035

10681036
#[tokio::test]
@@ -1097,8 +1065,7 @@ mod test {
10971065
let result = ipc.present_review(params).await;
10981066
assert!(result.is_ok());
10991067

1100-
let review_result = result.unwrap();
1101-
assert!(review_result.success);
1068+
result.unwrap();
11021069
}
11031070

11041071
#[tokio::test]

server/src/server.rs

Lines changed: 10 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ impl DialecticServer {
217217
)
218218
.await;
219219

220-
let result = self.ipc.present_review(params).await.map_err(|e| {
220+
self.ipc.present_review(params).await.map_err(|e| {
221221
McpError::internal_error(
222222
"IPC communication failed",
223223
Some(serde_json::json!({
@@ -226,38 +226,16 @@ impl DialecticServer {
226226
)
227227
})?;
228228

229-
if result.success {
230-
self.ipc
231-
.send_log(
232-
LogLevel::Info,
233-
"Review successfully displayed in VSCode".to_string(),
234-
)
235-
.await;
236-
237-
let message = result
238-
.message
239-
.unwrap_or_else(|| "Review successfully displayed in VSCode".to_string());
240-
241-
Ok(CallToolResult::success(vec![Content::text(message)]))
242-
} else {
243-
let error_msg = result
244-
.message
245-
.unwrap_or_else(|| "Unknown error".to_string());
246-
247-
self.ipc
248-
.send_log(
249-
LogLevel::Error,
250-
format!("Failed to display review: {}", error_msg),
251-
)
252-
.await;
229+
self.ipc
230+
.send_log(
231+
LogLevel::Info,
232+
"Review successfully displayed in VSCode".to_string(),
233+
)
234+
.await;
253235

254-
Err(McpError::internal_error(
255-
"Failed to display review",
256-
Some(serde_json::json!({
257-
"error": format!("Failed to display review: {}", error_msg)
258-
})),
259-
))
260-
}
236+
Ok(CallToolResult::success(vec![Content::text(
237+
"Review successfully displayed in VSCode",
238+
)]))
261239
}
262240

263241
/// Get the currently selected text from any active editor in VSCode

server/src/types.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,6 @@ impl Default for ReviewMode {
4242
}
4343
}
4444

45-
/// Response from the present-review tool
46-
#[derive(Debug, Clone, Deserialize, Serialize)]
47-
pub struct PresentReviewResult {
48-
/// Whether the review was successfully presented
49-
pub success: bool,
50-
51-
/// Optional message about the operation
52-
pub message: Option<String>,
53-
}
54-
5545
/// Parameters for log messages sent via IPC
5646
#[derive(Debug, Clone, Deserialize, Serialize)]
5747
pub struct LogParams {

0 commit comments

Comments
 (0)