use std::sync::Arc;
use serde_json::{Map, Value, json};
use crate::application::bridge::{BridgeError, BridgePeer, SendFuture};
use crate::application::client::{ClientTransport, TransportError};
use crate::domain::ir::JsonObject;
#[derive(Clone)]
pub struct TransportPeer {
transport: Arc<dyn ClientTransport>,
}
impl core::fmt::Debug for TransportPeer {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("TransportPeer").finish_non_exhaustive()
}
}
impl TransportPeer {
#[must_use]
pub fn new(transport: Arc<dyn ClientTransport>) -> Self {
Self { transport }
}
}
fn parts(envelope: &Map<String, Value>) -> Result<(&str, JsonObject), BridgeError> {
let method = envelope
.get("method")
.and_then(Value::as_str)
.ok_or_else(|| BridgeError::Unreachable {
reason: "the envelope has no method to forward".to_owned(),
})?;
let params = match envelope.get("params") {
None | Some(Value::Null) => json!({}),
Some(other) => other.clone(),
};
let params = JsonObject::from_value(params).map_err(|error| BridgeError::Unreachable {
reason: format!("the params are not a JSON object: {error}"),
})?;
Ok((method, params))
}
fn unreachable(error: impl core::fmt::Display) -> BridgeError {
BridgeError::Unreachable {
reason: error.to_string(),
}
}
impl BridgePeer for TransportPeer {
fn send(
&self,
envelope: Map<String, Value>,
) -> SendFuture<'_, Result<Map<String, Value>, BridgeError>> {
Box::pin(async move {
let (method, params) = parts(&envelope)?;
let id = envelope.get("id").cloned().unwrap_or(Value::Null);
let mut reply = Map::new();
reply.insert("jsonrpc".to_owned(), Value::from("2.0"));
reply.insert("id".to_owned(), id);
match self.transport.send(method, ¶ms).await {
Ok(result) => {
let result: Value =
serde_json::from_str(result.as_str()).map_err(unreachable)?;
reply.insert("result".to_owned(), result);
}
Err(TransportError::Refused {
code,
message,
data,
}) => {
let mut error = Map::new();
error.insert("code".to_owned(), Value::from(code));
error.insert("message".to_owned(), Value::from(message));
if let Some(data) = data
&& let Ok(data) = serde_json::from_str::<Value>(&data)
{
error.insert("data".to_owned(), data);
}
reply.insert("error".to_owned(), Value::Object(error));
}
Err(other) => return Err(unreachable(other)),
}
Ok(reply)
})
}
fn notify(&self, envelope: Map<String, Value>) -> SendFuture<'_, Result<(), BridgeError>> {
Box::pin(async move {
let (method, params) = parts(&envelope)?;
self.transport
.notify(method, ¶ms)
.await
.map_err(unreachable)
})
}
fn close(&self) -> SendFuture<'_, Result<(), BridgeError>> {
Box::pin(async { Ok(()) })
}
}
#[cfg(test)]
mod tests;