use crate::types::{JSONRPC_VERSION, Message, RequestId};
#[cfg(feature = "server")]
use crate::{
error::Error,
types::{FromRequest, Request},
};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
pub use log_message::{LogMessage, LoggingLevel, SetLevelRequestParams};
#[cfg(feature = "server")]
use crate::app::handler::{FromHandlerParams, HandlerParams};
pub use progress::ProgressNotification;
#[cfg(feature = "tracing")]
pub use formatter::NotificationFormatter;
#[cfg(feature = "tracing")]
pub mod fmt;
#[cfg(feature = "tracing")]
mod formatter;
mod log_message;
mod progress;
pub mod commands {
pub const INITIALIZED: &str = "notifications/initialized";
pub const CANCELLED: &str = "notifications/cancelled";
pub const MESSAGE: &str = "notifications/message";
pub const PROGRESS: &str = "notifications/progress";
pub const STDERR: &str = "notifications/stderr";
pub const SET_LOG_LEVEL: &str = "logging/setLevel";
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Notification {
pub jsonrpc: String,
pub method: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<serde_json::Value>,
#[serde(skip)]
pub session_id: Option<uuid::Uuid>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CancelledNotificationParams {
#[serde(rename = "requestId")]
pub request_id: RequestId,
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
}
impl From<Notification> for Message {
#[inline]
fn from(notification: Notification) -> Self {
Self::Notification(notification)
}
}
#[cfg(feature = "server")]
impl FromHandlerParams for CancelledNotificationParams {
#[inline]
fn from_params(params: &HandlerParams) -> Result<Self, Error> {
let req = Request::from_params(params)?;
Self::from_request(req)
}
}
impl Notification {
#[inline]
pub fn new(method: &str, params: Option<serde_json::Value>) -> Self {
Self {
jsonrpc: JSONRPC_VERSION.into(),
session_id: None,
method: method.into(),
params,
}
}
pub fn full_id(&self) -> RequestId {
let id = RequestId::default();
if let Some(session_id) = self.session_id {
id.concat(RequestId::Uuid(session_id))
} else {
id
}
}
#[inline]
pub fn params<T: DeserializeOwned>(&self) -> Option<T> {
match self.params {
Some(ref params) => serde_json::from_value(params.clone()).ok(),
None => None,
}
}
#[inline]
#[cfg(feature = "tracing")]
pub fn write(self) {
let is_stderr = self.is_stderr();
let Some(params) = self.params else {
return;
};
if is_stderr {
Self::write_err_internal(params);
} else {
match serde_json::from_value::<LogMessage>(params.clone()) {
Ok(log) => log.write(),
Err(err) => tracing::error!(logger = "neva", "{}", err),
}
}
}
#[inline]
pub fn is_stderr(&self) -> bool {
self.method.as_str() == commands::STDERR
}
#[inline]
#[cfg(feature = "tracing")]
pub fn write_err(self) {
if let Some(params) = self.params {
Self::write_err_internal(params)
}
}
pub fn to_json(self) -> String {
serde_json::to_string(&self).unwrap()
}
#[inline]
#[cfg(feature = "tracing")]
fn write_err_internal(params: serde_json::Value) {
let err = params.get("content").unwrap_or(¶ms);
tracing::error!("{}", err);
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn it_creates_new_notification() {
let notification = Notification::new("test", Some(json!({ "param": "value" })));
assert_eq!(notification.jsonrpc, "2.0");
assert_eq!(notification.method, "test");
let params_json = serde_json::to_string(¬ification.params.unwrap()).unwrap();
assert_eq!(params_json, r#"{"param":"value"}"#);
}
}