use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProgressNotification {
#[serde(rename = "progressToken")]
pub progress_token: String,
pub progress: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub total: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LogNotification {
pub level: LogLevel,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub logger: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum LogLevel {
Debug,
Info,
Warning,
Error,
}
impl std::fmt::Display for LogLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
LogLevel::Debug => write!(f, "debug"),
LogLevel::Info => write!(f, "info"),
LogLevel::Warning => write!(f, "warning"),
LogLevel::Error => write!(f, "error"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CancelledNotification {
#[serde(rename = "requestId")]
pub request_id: Value,
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_progress_notification() {
let n = ProgressNotification {
progress_token: "tok-1".into(),
progress: 50.0,
total: Some(100.0),
message: Some("halfway".into()),
};
let json = serde_json::to_value(&n);
assert!(json.is_ok());
}
#[test]
fn test_log_notification() {
let n = LogNotification {
level: LogLevel::Info,
message: "server started".into(),
data: None,
logger: Some("mcp".into()),
};
let json = serde_json::to_value(&n);
assert!(json.is_ok());
}
#[test]
fn test_log_level_display() {
assert_eq!(format!("{}", LogLevel::Debug), "debug");
assert_eq!(format!("{}", LogLevel::Warning), "warning");
}
#[test]
fn test_cancelled_notification() {
let n = CancelledNotification {
request_id: json!(42),
reason: Some("timeout".into()),
};
let json = serde_json::to_value(&n);
assert!(json.is_ok());
}
#[test]
fn test_log_level_serde_roundtrip() {
let level = LogLevel::Warning;
let s = serde_json::to_string(&level);
assert!(s.is_ok());
let serialized = match s {
Ok(value) => value,
Err(err) => panic!("failed to serialize log level: {err}"),
};
let parsed: Result<LogLevel, _> = serde_json::from_str(&serialized);
assert!(parsed.is_ok());
let parsed_level = match parsed {
Ok(value) => value,
Err(err) => panic!("failed to parse serialized log level: {err}"),
};
assert_eq!(parsed_level, LogLevel::Warning);
}
}