use crate::error::Result;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum TransportMessage {
Request {
id: crate::types::RequestId,
request: crate::types::Request,
},
Response(crate::types::JSONRPCResponse),
Notification(crate::types::Notification),
}
pub fn serialize_message(message: &TransportMessage) -> Result<Vec<u8>> {
use crate::error::TransportError;
match message {
TransportMessage::Request { id, request } => {
let jsonrpc_request = crate::shared::create_request(id.clone(), request.clone());
serde_json::to_vec(&jsonrpc_request).map_err(|e| {
TransportError::InvalidMessage(format!("Failed to serialize request: {}", e)).into()
})
},
TransportMessage::Response(response) => serde_json::to_vec(response).map_err(|e| {
TransportError::InvalidMessage(format!("Failed to serialize response: {}", e)).into()
}),
TransportMessage::Notification(notification) => {
let jsonrpc_notification = crate::shared::create_notification(notification.clone());
serde_json::to_vec(&jsonrpc_notification).map_err(|e| {
TransportError::InvalidMessage(format!("Failed to serialize notification: {}", e))
.into()
})
},
}
}
pub fn parse_message(buffer: &[u8]) -> Result<TransportMessage> {
use crate::error::TransportError;
let json_value: serde_json::Value = serde_json::from_slice(buffer)
.map_err(|e| TransportError::InvalidMessage(format!("Invalid JSON: {}", e)))?;
if json_value.get("method").is_some() {
parse_method_message(json_value)
} else if json_value.get("result").is_some() || json_value.get("error").is_some() {
parse_response_message(json_value)
} else {
Err(TransportError::InvalidMessage("Unknown message type".to_string()).into())
}
}
fn parse_method_message(json_value: serde_json::Value) -> Result<TransportMessage> {
use crate::error::TransportError;
if json_value.get("id").is_some() {
let request: crate::types::JSONRPCRequest<serde_json::Value> =
serde_json::from_value(json_value)
.map_err(|e| TransportError::InvalidMessage(format!("Invalid request: {}", e)))?;
let parsed_request = crate::shared::parse_request(request)
.map_err(|e| TransportError::InvalidMessage(format!("Invalid request: {}", e)))?;
Ok(TransportMessage::Request {
id: parsed_request.0,
request: parsed_request.1,
})
} else {
let parsed_notification = crate::shared::parse_notification(json_value)
.map_err(|e| TransportError::InvalidMessage(format!("Invalid notification: {}", e)))?;
Ok(TransportMessage::Notification(parsed_notification))
}
}
fn parse_response_message(json_value: serde_json::Value) -> Result<TransportMessage> {
use crate::error::TransportError;
let response: crate::types::JSONRPCResponse = serde_json::from_value(json_value)
.map_err(|e| TransportError::InvalidMessage(format!("Invalid response: {}", e)))?;
Ok(TransportMessage::Response(response))
}
#[derive(Debug, Clone, Default)]
pub struct MessageMetadata {
pub content_type: Option<String>,
pub priority: Option<MessagePriority>,
pub flush: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum MessagePriority {
Low,
#[default]
Normal,
High,
}
#[cfg(not(target_arch = "wasm32"))]
#[async_trait]
pub trait Transport: Send + Sync + Debug {
async fn send(&mut self, message: TransportMessage) -> Result<()>;
async fn receive(&mut self) -> Result<TransportMessage>;
async fn close(&mut self) -> Result<()>;
fn is_connected(&self) -> bool {
true
}
fn transport_type(&self) -> &'static str {
"unknown"
}
}
#[cfg(target_arch = "wasm32")]
#[async_trait(?Send)]
pub trait Transport: Debug {
async fn send(&mut self, message: TransportMessage) -> Result<()>;
async fn receive(&mut self) -> Result<TransportMessage>;
async fn close(&mut self) -> Result<()>;
fn is_connected(&self) -> bool {
true
}
fn transport_type(&self) -> &'static str {
"unknown"
}
}
#[derive(Debug, Clone, Default)]
pub struct SendOptions {
pub priority: Option<MessagePriority>,
pub flush: bool,
pub timeout: Option<std::time::Duration>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn priority_ordering() {
assert!(MessagePriority::Low < MessagePriority::Normal);
assert!(MessagePriority::Normal < MessagePriority::High);
}
#[test]
fn serialize_request_is_flat_jsonrpc_frame() {
use crate::types::{Request, RequestId};
let msg = TransportMessage::Request {
id: RequestId::from(1i64),
request: Request::Client(Box::new(crate::types::ClientRequest::Ping)),
};
let bytes = serialize_message(&msg).expect("serialize");
let v: serde_json::Value = serde_json::from_slice(&bytes).expect("valid json");
assert_eq!(v["jsonrpc"], "2.0", "must carry the JSON-RPC version");
assert!(v.get("method").is_some(), "must have a top-level method");
assert!(
v.get("request").is_none(),
"must NOT emit the untagged enum wrapper: {v}"
);
}
#[test]
fn parse_message_roundtrips_request_and_classifies() {
use crate::types::{Request, RequestId};
let msg = TransportMessage::Request {
id: RequestId::from(7i64),
request: Request::Client(Box::new(crate::types::ClientRequest::Ping)),
};
let bytes = serialize_message(&msg).expect("serialize");
match parse_message(&bytes).expect("parse") {
TransportMessage::Request { id, .. } => assert_eq!(id, RequestId::from(7i64)),
other => panic!("expected Request, got {other:?}"),
}
let resp = br#"{"jsonrpc":"2.0","id":1,"result":{}}"#;
assert!(matches!(
parse_message(resp).expect("parse response"),
TransportMessage::Response(_)
));
assert!(parse_message(br#"{"id":1,"request":{}}"#).is_err());
}
}