use crate::error::Error;
use crate::types::{JSONRPC_VERSION, Message, RequestId};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
#[cfg(feature = "http-server")]
use volga::headers::HeaderMap;
pub use error_details::ErrorDetails;
pub use into_response::IntoResponse;
mod error_details;
mod into_response;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Response {
Ok(OkResponse),
Err(ErrorResponse),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OkResponse {
pub jsonrpc: String,
#[serde(default)]
pub id: RequestId,
pub result: Value,
#[serde(skip)]
pub session_id: Option<uuid::Uuid>,
#[serde(skip)]
#[cfg(feature = "http-server")]
pub headers: HeaderMap,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorResponse {
pub jsonrpc: String,
#[serde(default)]
pub id: RequestId,
pub error: ErrorDetails,
#[serde(skip)]
pub session_id: Option<uuid::Uuid>,
#[serde(skip)]
#[cfg(feature = "http-server")]
pub headers: HeaderMap,
}
impl From<Response> for Message {
#[inline]
fn from(response: Response) -> Self {
Self::Response(response)
}
}
impl Response {
pub fn success(id: RequestId, result: Value) -> Self {
Response::Ok(OkResponse {
jsonrpc: JSONRPC_VERSION.to_string(),
session_id: None,
#[cfg(feature = "http-server")]
headers: HeaderMap::with_capacity(8),
id,
result,
})
}
pub fn empty(id: RequestId) -> Self {
Response::Ok(OkResponse {
jsonrpc: JSONRPC_VERSION.to_string(),
session_id: None,
#[cfg(feature = "http-server")]
headers: HeaderMap::new(),
id,
result: json!({}),
})
}
pub fn error(id: RequestId, error: Error) -> Self {
Response::Err(ErrorResponse {
jsonrpc: JSONRPC_VERSION.to_string(),
session_id: None,
#[cfg(feature = "http-server")]
headers: HeaderMap::with_capacity(8),
id,
error: error.into(),
})
}
pub fn id(&self) -> &RequestId {
match &self {
Response::Ok(ok) => &ok.id,
Response::Err(err) => &err.id,
}
}
pub fn full_id(&self) -> RequestId {
let id = self.id().clone();
if let Some(session_id) = self.session_id() {
id.concat(RequestId::Uuid(*session_id))
} else {
id
}
}
pub fn set_id(mut self, id: RequestId) -> Self {
match &mut self {
Response::Ok(ok) => ok.id = id,
Response::Err(err) => err.id = id,
}
self
}
#[inline]
pub fn session_id(&self) -> Option<&uuid::Uuid> {
match &self {
Response::Ok(ok) => ok.session_id.as_ref(),
Response::Err(err) => err.session_id.as_ref(),
}
}
pub fn set_session_id(mut self, id: uuid::Uuid) -> Self {
match &mut self {
Response::Ok(ok) => ok.session_id = Some(id),
Response::Err(err) => err.session_id = Some(id),
}
self
}
#[cfg(feature = "http-server")]
pub fn set_headers(mut self, headers: HeaderMap) -> Self {
match &mut self {
Response::Ok(ok) => ok.headers = headers,
Response::Err(err) => err.headers = headers,
}
self
}
pub fn into_result<T: DeserializeOwned>(self) -> Result<T, Error> {
match self {
Response::Ok(ok) => serde_json::from_value::<T>(ok.result).map_err(Into::into),
Response::Err(err) => Err(err.error.into()),
}
}
}
#[cfg(test)]
mod tests {
use super::Response;
use crate::{error::Error, types::RequestId};
#[test]
fn it_deserializes_successful_response_with_int_id_to_json() {
let resp = Response::success(RequestId::Number(42), serde_json::json!({ "key": "test" }));
let json = serde_json::to_string(&resp).unwrap();
assert_eq!(json, r#"{"jsonrpc":"2.0","id":42,"result":{"key":"test"}}"#);
}
#[test]
fn it_deserializes_error_response_with_string_id_to_json() {
let resp = Response::error(
RequestId::String("id".into()),
Error::new(-32603, "some error message"),
);
let json = serde_json::to_string(&resp).unwrap();
assert_eq!(
json,
r#"{"jsonrpc":"2.0","id":"id","error":{"code":-32603,"message":"some error message","data":null}}"#
);
}
}