//! Outgoing core calls and notification callbacks. use super::*; /// An outgoing request builder. #[must_use = "await the call to send it"] pub struct Call { method: String, params: Value, detach: bool, notify: Option, pub(super) unlock: Option>, } /// Build an outgoing call with object parameters. pub fn call(method: impl Into, params: impl serde::Serialize) -> Call { Call { method: method.into(), params: serde_json::to_value(params).expect("serializable params"), detach: false, notify: None, unlock: None, } } impl Call { /// Detach the child process from its caller. pub fn detach(mut self) -> Self { self.detach = true; self } /// Receive progress notifications before the result arrives. pub fn on_notify(mut self, f: impl FnMut(&str, Value) + 'static) -> Self { self.notify = Some(Box::new(f)); self } } impl IntoFuture for Call { type Output = Result; type IntoFuture = LocalBoxFuture<'static, Self::Output>; fn into_future(self) -> Self::IntoFuture { Box::pin(async move { if !self.params.is_object() { return Err(Error::new(-32602, "params must be an object")); } let (tx, rx) = oneshot::channel(); let id = STATE.with(|s| { let mut s = s.borrow_mut(); if !s.active || s.stopping { return Err(Error::new( -32000, "no core transport (or stdin reached EOF)", )); } s.next += 1; let id = s.next; s.pending.insert( id, Pending { tx, notify: self.notify, unlock: self.unlock, }, ); Ok(id) })?; let (method, params) = if self.detach { ( "core.call".to_string(), json!({"method":self.method,"params":self.params,"detach":true}), ) } else { (self.method, self.params) }; if let Err(e) = send(json!({"jsonrpc":"2.0","id":id,"method":method,"params":params})) { STATE.with(|s| s.borrow_mut().pending.remove(&id)); return Err(e); } rx.await .unwrap_or_else(|_| Err(Error::new(-32000, "core transport closed"))) }) } }