use serde::{Deserialize, Serialize};
use serde_json::Value;
use tracing::error;
use crate::plugin_actor::Method;
pub const JSONRPC_VERSION: &str = "2.0";
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Id {
Number(i64),
String(String),
Null,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Request {
#[serde(default = "default_version")]
pub jsonrpc: String,
pub method: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<Id>,
}
fn default_version() -> String {
JSONRPC_VERSION.to_owned()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Error {
pub code: i64,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Response {
#[serde(default = "default_version")]
pub jsonrpc: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<Error>,
pub id: Id,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Message {
Request(Request),
Response(Response),
}
impl Request {
pub fn notification(method: impl Into<String>, params: Option<Value>) -> Self {
Self {
jsonrpc: JSONRPC_VERSION.to_owned(),
method: method.into(),
params,
id: None,
}
}
pub fn call<M: Into<Method>>(id: Id, method: M, params: Option<Value>) -> Self {
let method = method.into();
Self {
jsonrpc: JSONRPC_VERSION.to_owned(),
method: method.to_string(),
params,
id: Some(id),
}
}
}
impl Response {
pub fn success(id: Id, result: Value) -> Self {
Self {
jsonrpc: JSONRPC_VERSION.to_owned(),
result: Some(result),
error: None,
id,
}
}
pub fn fail(id: Id, code: i64, message: impl Into<String>, data: Option<Value>) -> Self {
Self {
jsonrpc: JSONRPC_VERSION.to_owned(),
result: None,
error: Some(Error {
code,
message: message.into(),
data,
}),
id,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PluginMethod {
MessageIn,
MessageOut,
Init,
Start,
Drain,
Stop,
Status,
Health,
WaitUntilDrained,
Capabilities,
SetConfig,
SetSecrets,
ListConfigKeys,
ListSecretKeys,
GetSession,
InvalidateSession,
}
impl PluginMethod {
pub const fn as_str(&self) -> &'static str {
match self {
PluginMethod::MessageIn => "messageIn",
PluginMethod::MessageOut => "messageOut",
PluginMethod::Init => "init",
PluginMethod::Start => "start",
PluginMethod::Drain => "drain",
PluginMethod::Stop => "stop",
PluginMethod::Status => "status",
PluginMethod::Health => "health",
PluginMethod::Capabilities => "capabilities",
PluginMethod::WaitUntilDrained => "waitUntilDrained",
PluginMethod::SetConfig => "setConfig",
PluginMethod::SetSecrets => "setSecrets",
PluginMethod::ListConfigKeys => "listConfigKeys",
PluginMethod::ListSecretKeys => "listSecretKeys",
PluginMethod::GetSession => "getSession",
PluginMethod::InvalidateSession => "invalidateSession",
}
}
}
impl From<PluginMethod> for String {
fn from(m: PluginMethod) -> Self {
m.as_str().to_owned()
}
}
impl std::str::FromStr for PluginMethod {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"messageIn" => Ok(PluginMethod::MessageIn),
"messageOut" => Ok(PluginMethod::MessageOut),
"init" => Ok(PluginMethod::Init),
"start" => Ok(PluginMethod::Start),
"drain" => Ok(PluginMethod::Drain),
"stop" => Ok(PluginMethod::Stop),
"status" => Ok(PluginMethod::Status),
"health" => Ok(PluginMethod::Health),
"capabilities" => Ok(PluginMethod::Capabilities),
"waitUntilDrained" => Ok(PluginMethod::WaitUntilDrained),
"setConfig" => Ok(PluginMethod::SetConfig),
"setSecrets" => Ok(PluginMethod::SetSecrets),
"listConfigKeys" => Ok(PluginMethod::ListConfigKeys),
"listSecretKeys" => Ok(PluginMethod::ListSecretKeys),
"getSession" => Ok(PluginMethod::GetSession),
"invalidateSession" => Ok(PluginMethod::InvalidateSession),
other => {
error!("Fix bug in PluginMethod FromStr for {}",other);
Err(())
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn roundtrip_request() {
let req = Request::call(Id::Number(1), Method::Name, Some(json!({"msg": "hi"})));
let s = serde_json::to_string(&req).unwrap();
let de: Request = serde_json::from_str(&s).unwrap();
assert_eq!(de.method, "name");
}
#[test]
fn roundtrip_response() {
let resp = Response::success(Id::String("abc".into()), json!({"ok": true}));
let s = serde_json::to_string(&resp).unwrap();
let de: Response = serde_json::from_str(&s).unwrap();
assert_eq!(de.result.unwrap()["ok"], json!(true));
}
#[test]
fn plugin_method_parse() {
let m: PluginMethod = "health".parse().unwrap();
assert_eq!(m, PluginMethod::Health);
assert_eq!(m.as_str(), "health");
}
}