use serde_json::Value;
use std::rc::Rc;
pub trait RpcChannel {
fn send_notification(&self, method: String, notification: Value);
fn send_request(&self, method: String, params: Value);
}
#[derive(Clone)]
pub struct SharedRpcChannel {
chan: Rc<dyn RpcChannel>,
}
impl SharedRpcChannel {
pub fn new(chan: Rc<dyn RpcChannel>) -> Self {
Self { chan }
}
pub fn send_notification(
&self,
method: impl Into<String>,
notification: impl serde::ser::Serialize,
) {
self.chan
.send_notification(method.into(), serde_json::to_value(¬ification).unwrap())
}
pub fn send_request(&self, method: impl Into<String>, params: impl serde::ser::Serialize) {
self.chan
.send_request(method.into(), serde_json::to_value(¶ms).unwrap())
}
}
#[cfg(test)]
pub mod test_support {
use pretty_assertions::assert_eq;
use serde_json::Value;
use std::cell::RefCell;
use std::collections::VecDeque;
use std::rc::Rc;
#[derive(Debug)]
pub enum RpcExpected {
Notification {
method: String,
notification: serde_json::Value,
},
NotificationContainsString { method: String, contains: String },
Request {
method: String,
params: serde_json::Value,
},
}
#[derive(Clone)]
pub struct RpcMock {
expected: Rc<RefCell<VecDeque<RpcExpected>>>,
}
impl RpcMock {
pub fn new() -> RpcMock {
RpcMock {
expected: Rc::new(RefCell::new(VecDeque::new())),
}
}
pub fn expect_notification(
&self,
method: impl Into<String>,
notification: impl serde::ser::Serialize,
) {
self.expected
.borrow_mut()
.push_back(RpcExpected::Notification {
method: method.into(),
notification: serde_json::to_value(notification).unwrap(),
});
}
fn expect_notification_contains(
&self,
method: impl Into<String>,
contains: impl Into<String>,
) {
self.expected
.borrow_mut()
.push_back(RpcExpected::NotificationContainsString {
method: method.into(),
contains: contains.into(),
});
}
pub fn expect_request(
&self,
method: impl Into<String>,
params: impl serde::ser::Serialize,
) {
self.expected.borrow_mut().push_back(RpcExpected::Request {
method: method.into(),
params: serde_json::to_value(params).unwrap(),
});
}
pub fn expect_error_contains(&self, contains: impl Into<String>) {
let contains = contains.into();
self.expect_notification_contains("window/showMessage", contains.clone());
self.expect_notification_contains("window/logMessage", contains);
}
pub fn expect_warning_contains(&self, contains: impl Into<String>) {
self.expect_error_contains(contains)
}
pub fn expect_message_contains(&self, contains: impl Into<String>) {
let contains = contains.into();
self.expect_notification_contains("window/logMessage", contains);
}
}
impl Drop for RpcMock {
fn drop(&mut self) {
if !std::thread::panicking() {
let expected = self.expected.replace(VecDeque::new());
if !expected.is_empty() {
panic!("Not all expected data was consumed\n{expected:#?}");
}
}
}
}
fn contains_string(value: &serde_json::Value, string: &str) -> bool {
match value {
serde_json::Value::Array(values) => {
values.iter().any(|value| contains_string(value, string))
}
serde_json::Value::Object(map) => {
map.values().any(|value| contains_string(value, string))
}
serde_json::Value::String(got_string) => got_string.contains(string),
serde_json::Value::Null => false,
serde_json::Value::Bool(..) => false,
serde_json::Value::Number(..) => false,
}
}
impl super::RpcChannel for RpcMock {
fn send_notification(&self, method: String, notification: Value) {
let expected = self
.expected
.borrow_mut()
.pop_front()
.ok_or_else(|| {
panic!("No expected value, got notification method={method} {notification:?}")
})
.unwrap();
match expected {
RpcExpected::Notification {
method: exp_method,
notification: exp_notification,
} => {
assert_eq!((method, notification), (exp_method, exp_notification));
}
RpcExpected::NotificationContainsString {
method: exp_method,
contains,
} => {
assert_eq!(
method, exp_method,
"{:?} contains {:?}",
notification, contains
);
if !contains_string(¬ification, &contains) {
panic!("{notification:?} does not contain sub-string {contains:?}");
}
}
_ => panic!("Expected {expected:?}, got notification {method} {notification:?}"),
}
}
fn send_request(&self, method: String, params: Value) {
let expected = self
.expected
.borrow_mut()
.pop_front()
.ok_or_else(|| panic!("No expected value, got request method={method} {params:?}"))
.unwrap();
match expected {
RpcExpected::Request {
method: exp_method,
params: exp_params,
} => {
assert_eq!((method, params), (exp_method, exp_params));
}
_ => panic!("Expected {expected:?}, got request {method} {params:?}"),
}
}
}
}