sausage-plugin part 2: import! with typed clients (sausage_interface::client_rs), TCP endpoints with rpc.endpoint, examples

Part 1 fixes: complete stdout writes, buffered stdin, Rust type names
in schemas, enum variant docs, -32603 for serde errors in handlers,
crate docs and modules. End-to-end check of a generated consumer through
a fake core.
This commit is contained in:
2026-10-23 00:35:45 +01:00
parent ae1cb4cc99
commit 8d3c163992
42 changed files with 3020 additions and 856 deletions
+78
View File
@@ -0,0 +1,78 @@
//! Outgoing core calls and notification callbacks.
use super::*;
/// An outgoing request builder.
pub struct Call {
method: String,
params: Value,
detach: bool,
notify: Option<Notify>,
pub(super) unlock: Option<std::rc::Rc<Lease>>,
}
/// Build an outgoing call with object parameters.
pub fn call(method: impl Into<String>, 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<Value>;
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(
-32002,
"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(-32002, "core transport closed")))
})
}
}