use async_trait::async_trait;
use std::time::Duration;
#[async_trait]
pub trait ServerOutbound: Send + Sync {
async fn outbound_request(
&self,
method: &str,
params: serde_json::Value,
timeout: Duration,
) -> Result<serde_json::Value, ServerOutboundError>;
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum ServerOutboundError {
#[error("outbound request timed out")]
Timeout,
#[error("peer returned error: code={code} message={message}")]
PeerError {
code: i64,
message: String,
},
#[error("transport closed")]
TransportClosed,
#[error("outbound channel unsupported on this transport")]
Unsupported,
#[error("outbound frame serialisation failed: {0}")]
Serialisation(#[source] serde_json::Error),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn display_messages_are_stable() {
assert_eq!(
ServerOutboundError::Timeout.to_string(),
"outbound request timed out"
);
assert_eq!(
ServerOutboundError::PeerError {
code: -32601,
message: "Method not found".to_string(),
}
.to_string(),
"peer returned error: code=-32601 message=Method not found"
);
assert_eq!(
ServerOutboundError::TransportClosed.to_string(),
"transport closed"
);
assert_eq!(
ServerOutboundError::Unsupported.to_string(),
"outbound channel unsupported on this transport"
);
}
#[test]
fn serialisation_variant_display_starts_with_prefix() {
let serde_err =
serde_json::from_str::<serde_json::Value>("{invalid}").unwrap_err();
let msg = ServerOutboundError::Serialisation(serde_err).to_string();
assert!(
msg.starts_with("outbound frame serialisation failed: "),
"unexpected display message: {msg}"
);
}
#[test]
fn trait_object_is_send_and_sync() {
fn assert_send_sync<T: Send + Sync + ?Sized>() {}
assert_send_sync::<dyn ServerOutbound>();
}
#[tokio::test]
async fn outbound_request_stub_dispatches_and_returns_value() {
struct StubOutbound;
#[async_trait::async_trait]
impl ServerOutbound for StubOutbound {
async fn outbound_request(
&self,
_method: &str,
_params: serde_json::Value,
_timeout: Duration,
) -> Result<serde_json::Value, ServerOutboundError> {
Ok(serde_json::json!({"ok": true}))
}
}
let stub = StubOutbound;
let result = stub
.outbound_request("test/method", serde_json::Value::Null, Duration::from_secs(5))
.await
.unwrap();
assert_eq!(result["ok"], true);
}
}