use tokio_util::sync::CancellationToken;
use crate::error::{ErrorCode, ResponseError};
use crate::message::{Notification, Request, Response};
#[derive(Debug)]
#[non_exhaustive]
pub enum IncomingMessage {
Request(Request, CancellationToken),
Notification(Notification),
CancelHandled,
ResponseRouted,
ResponseUnknown(Response),
}
impl IncomingMessage {
#[must_use]
pub fn is_request(&self) -> bool {
matches!(self, Self::Request(_, _))
}
#[must_use]
pub fn is_notification(&self) -> bool {
matches!(self, Self::Notification(_))
}
#[must_use]
pub fn is_cancel_handled(&self) -> bool {
matches!(self, Self::CancelHandled)
}
#[must_use]
pub fn is_response_routed(&self) -> bool {
matches!(self, Self::ResponseRouted)
}
#[must_use]
pub fn is_response_unknown(&self) -> bool {
matches!(self, Self::ResponseUnknown(_))
}
}
#[must_use]
pub fn method_not_found_response(request: &Request) -> Response {
let error = ResponseError::new(
ErrorCode::MethodNotFound,
format!("Method not found: {}", request.method),
);
Response::err(request.id.clone(), error)
}
pub fn cancelled_response(id: impl Into<crate::RequestId>) -> Response {
let error = ResponseError::new(ErrorCode::RequestCancelled, "Request was cancelled");
Response::err(id, error)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ErrorCode;
use serde_json::json;
#[test]
fn method_not_found_response_creates_correct_error() {
let request = Request::new(42, "textDocument/unknown", Some(json!({"key": "value"})));
let response = method_not_found_response(&request);
assert!(response.error().is_some());
assert!(response.result().is_none());
assert_eq!(response.id, Some(42.into()));
let error = response.into_error().unwrap();
assert_eq!(error.code, ErrorCode::MethodNotFound as i32);
assert_eq!(error.code, -32601);
}
#[test]
fn method_not_found_response_includes_method_name() {
let request = Request::new(1, "custom/myMethod", None);
let response = method_not_found_response(&request);
let error = response.into_error().unwrap();
assert!(
error.message.contains("custom/myMethod"),
"Error message should contain the method name"
);
assert_eq!(error.message, "Method not found: custom/myMethod");
}
#[test]
fn method_not_found_response_with_string_id() {
let request = Request::new("request-abc-123", "test/method", None);
let response = method_not_found_response(&request);
assert_eq!(
response.id,
Some(crate::RequestId::String("request-abc-123".to_string()))
);
}
#[test]
fn incoming_message_variants_constructible() {
use tokio_util::sync::CancellationToken;
let request = Request::new(1, "test", None);
let notification = Notification::new("test", None);
let response = Response::ok(1, json!(null));
let token = CancellationToken::new();
let _req = IncomingMessage::Request(request, token);
let _notif = IncomingMessage::Notification(notification);
let _routed = IncomingMessage::ResponseRouted;
let _unknown = IncomingMessage::ResponseUnknown(response);
}
#[test]
fn incoming_message_is_debug() {
use tokio_util::sync::CancellationToken;
let request = Request::new(1, "test", None);
let token = CancellationToken::new();
let incoming = IncomingMessage::Request(request, token);
let debug_str = format!("{incoming:?}");
assert!(debug_str.contains("Request"));
}
#[test]
fn incoming_message_accessors_match_variants() {
let request =
IncomingMessage::Request(Request::new(1, "test", None), CancellationToken::new());
assert!(request.is_request());
assert!(!request.is_notification());
assert!(!request.is_response_routed());
assert!(!request.is_response_unknown());
assert!(!request.is_cancel_handled());
let notification = IncomingMessage::Notification(Notification::new("test", None));
assert!(notification.is_notification());
let routed = IncomingMessage::ResponseRouted;
assert!(routed.is_response_routed());
let unknown = IncomingMessage::ResponseUnknown(Response::ok(1, json!(null)));
assert!(unknown.is_response_unknown());
let cancelled = IncomingMessage::CancelHandled;
assert!(cancelled.is_cancel_handled());
}
use super::cancelled_response;
#[test]
fn cancelled_response_creates_correct_error() {
let response = cancelled_response(42);
assert!(response.error().is_some());
assert!(response.result().is_none());
assert_eq!(response.id, Some(42.into()));
let error = response.into_error().unwrap();
assert_eq!(error.code, ErrorCode::RequestCancelled as i32);
assert_eq!(error.code, -32800);
assert!(error.message.contains("cancelled"));
}
#[test]
fn cancelled_response_with_string_id() {
let response = cancelled_response("req-xyz");
assert_eq!(
response.id,
Some(crate::RequestId::String("req-xyz".to_string()))
);
assert!(response.error().is_some());
}
}