use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, AtomicI64, Ordering};
use std::sync::{Arc, Mutex, PoisonError};
use tokio::io::AsyncWrite;
use tokio::sync::oneshot;
use super::StdioSink;
use crate::application::server::context::{DynExchange, ExchangeError};
use crate::domain::ir::JsonObject;
type Answer = Result<JsonObject, ExchangeError>;
pub(super) struct StdioExchange<W> {
sink: Arc<StdioSink<W>>,
pending: Mutex<HashMap<i64, oneshot::Sender<Answer>>>,
next: AtomicI64,
closed: AtomicBool,
}
impl<W> StdioExchange<W> {
pub(super) fn new(sink: Arc<StdioSink<W>>) -> Self {
Self {
sink,
pending: Mutex::new(HashMap::new()),
next: AtomicI64::new(1),
closed: AtomicBool::new(false),
}
}
pub(super) fn resolve(&self, id: i64, answer: Answer) {
let waiter = self
.pending
.lock()
.unwrap_or_else(PoisonError::into_inner)
.remove(&id);
if let Some(waiter) = waiter {
let _ = waiter.send(answer);
}
}
pub(super) fn close(&self) {
self.closed.store(true, Ordering::SeqCst);
let waiting: Vec<_> = self
.pending
.lock()
.unwrap_or_else(PoisonError::into_inner)
.drain()
.collect();
for (_, waiter) in waiting {
let _ = waiter.send(Err(ExchangeError::new("the peer closed its input")));
}
}
}
pub(super) fn answer_of(reply: &serde_json::Value) -> Answer {
if let Some(failure) = reply.get("error") {
return Err(ExchangeError::new(failure.to_string()));
}
reply.get("result").map_or_else(
|| {
Err(ExchangeError::new(
"the answer carried neither a result nor an error",
))
},
|result| {
JsonObject::from_value(result.clone())
.map_err(|error| ExchangeError::new(error.to_string()))
},
)
}
impl<W> DynExchange for StdioExchange<W>
where
W: AsyncWrite + Unpin + Send + 'static,
{
fn exchange_boxed<'a>(
&'a self,
method: &'a str,
params: &'a JsonObject,
) -> core::pin::Pin<Box<dyn Future<Output = Answer> + Send + 'a>> {
Box::pin(async move {
if self.closed.load(Ordering::SeqCst) {
return Err(ExchangeError::new("the peer closed its input"));
}
let id = self.next.fetch_add(1, Ordering::Relaxed);
let (sender, receiver) = oneshot::channel();
self.pending
.lock()
.unwrap_or_else(PoisonError::into_inner)
.insert(id, sender);
let params: serde_json::Value =
serde_json::from_str(params.as_str()).unwrap_or(serde_json::Value::Null);
let request = serde_json::json!({
"jsonrpc": "2.0",
"id": id,
"method": method,
"params": params,
})
.to_string();
if self.sink.send(&request).await.is_err() {
self.pending
.lock()
.unwrap_or_else(PoisonError::into_inner)
.remove(&id);
return Err(ExchangeError::new("the request could not be written"));
}
receiver
.await
.unwrap_or_else(|_| Err(ExchangeError::new("the request was abandoned")))
})
}
}
#[cfg(test)]
mod tests;