sdk: readable clients (no Empty wrapper, no unused error module), documented examples with tests

This commit is contained in:
2026-10-23 01:05:19 +01:00
parent edc2d1a63b
commit 0516158883
15 changed files with 426 additions and 81 deletions
+63 -40
View File
@@ -33,10 +33,6 @@
"text"
],
"type": "object"
},
"Empty": {
"description": "Empty parameters for methods without arguments.",
"type": "object"
}
}
},
@@ -56,10 +52,61 @@
"type": "null"
}
},
"summary": "Publish a control endpoint until EOF.",
"x-sausage-params": {
"$ref": "#/components/schemas/Empty"
}
"summary": "Publish a control endpoint until EOF."
},
{
"description": "Forward detached progress through the real core.",
"name": "relay",
"paramStructure": "by-name",
"params": [],
"result": {
"name": "result",
"schema": {
"format": "int32",
"type": "integer"
}
},
"summary": "Forward detached progress through the real core."
},
{
"description": "Send progress before the final result.",
"name": "progress",
"paramStructure": "by-name",
"params": [],
"result": {
"name": "result",
"schema": {
"format": "int32",
"type": "integer"
}
},
"summary": "Send progress before the final result."
},
{
"description": "Start a detached service that survives this caller.",
"name": "launch",
"paramStructure": "by-name",
"params": [],
"result": {
"name": "result",
"schema": {
"type": "string"
}
},
"summary": "Start a detached service that survives this caller."
},
{
"description": "Keep the process alive until the core closes stdin.",
"name": "service",
"paramStructure": "by-name",
"params": [],
"result": {
"name": "result",
"schema": {
"type": "string"
}
},
"summary": "Keep the process alive until the core closes stdin."
},
{
"description": "Add two numbers.\n\nThe result is their sum.",
@@ -136,10 +183,7 @@
"type": "string"
}
},
"summary": "Return an internal serialization error.",
"x-sausage-params": {
"$ref": "#/components/schemas/Empty"
}
"summary": "Return an internal serialization error."
},
{
"description": "Return a deliberate error.",
@@ -152,10 +196,7 @@
"type": "string"
}
},
"summary": "Return a deliberate error.",
"x-sausage-params": {
"$ref": "#/components/schemas/Empty"
}
"summary": "Return a deliberate error."
},
{
"description": "Exercise calls, notifications, and advisory locks.",
@@ -168,10 +209,7 @@
"type": "string"
}
},
"summary": "Exercise calls, notifications, and advisory locks.",
"x-sausage-params": {
"$ref": "#/components/schemas/Empty"
}
"summary": "Exercise calls, notifications, and advisory locks."
},
{
"description": "Drop an acquisition before the core grants its lock.",
@@ -184,10 +222,7 @@
"type": "string"
}
},
"summary": "Drop an acquisition before the core grants its lock.",
"x-sausage-params": {
"$ref": "#/components/schemas/Empty"
}
"summary": "Drop an acquisition before the core grants its lock."
},
{
"description": "Wait until EOF, then run the stop hook.",
@@ -200,10 +235,7 @@
"type": "string"
}
},
"summary": "Wait until EOF, then run the stop hook.",
"x-sausage-params": {
"$ref": "#/components/schemas/Empty"
}
"summary": "Wait until EOF, then run the stop hook."
},
{
"description": "Keep the process alive with a background call.",
@@ -216,10 +248,7 @@
"type": "string"
}
},
"summary": "Keep the process alive with a background call.",
"x-sausage-params": {
"$ref": "#/components/schemas/Empty"
}
"summary": "Keep the process alive with a background call."
}
],
"openrpc": "1.2.6",
@@ -239,10 +268,7 @@
"type": "integer"
}
},
"summary": "Report progress and return a stable counter.",
"x-sausage-params": {
"$ref": "#/components/schemas/Empty"
}
"summary": "Report progress and return a stable counter."
},
{
"description": "End the endpoint process after the response.",
@@ -255,10 +281,7 @@
"type": "null"
}
},
"summary": "End the endpoint process after the response.",
"x-sausage-params": {
"$ref": "#/components/schemas/Empty"
}
"summary": "End the endpoint process after the response."
}
]
}
+17
View File
@@ -11,6 +11,23 @@ impl Consumer {
.on_notify(|name, _| eprintln!("typed progress: {name}"))
.await? as i32)
}
/// Connect to a world endpoint from the real core.
async fn world_endpoint(&self) -> Result<u64> {
let control = probe::control::connect("/state/control").await?;
let received = std::rc::Rc::new(std::cell::Cell::new(false));
let progress = received.clone();
let result = control
.stats()
.on_notify(move |name, value| {
progress.set(name == "endpoint-progress" && value["value"] == 7);
})
.await?;
assert!(
received.get(),
"the endpoint must deliver progress before its result"
);
Ok(result)
}
/// Connect to the provider's endpoint and receive its notification.
async fn endpoint(&self) -> Result<u64> {
let control = probe::control::connect("/endpoint").await?;
+26
View File
@@ -28,6 +28,32 @@ impl Probe {
async fn serve(&self) -> Result<()> {
sausage_plugin::listen("/endpoint", std::rc::Rc::new(Control)).await
}
/// Forward detached progress through the real core.
async fn relay(&self) -> Result<i32> {
call("probe.progress", json!({}))
.detach()
.on_notify(move |name, params| {
Ctx::default().notify(name, params).unwrap();
})
.await
.map(|value| value.as_i64().unwrap() as i32)
}
/// Send progress before the final result.
fn progress(&self, ctx: &Ctx) -> Result<i32> {
ctx.notify("step", json!({"done":true}))?;
Ok(42)
}
/// Start a detached service that survives this caller.
async fn launch(&self) -> Result<String> {
Ok(sausage_plugin::serde_json::from_value(
call("probe.service", json!({})).detach().await?,
)?)
}
/// Keep the process alive until the core closes stdin.
async fn service(&self) -> Result<String> {
sausage_plugin::listen("/state/control", std::rc::Rc::new(Control)).await?;
Ok(sausage_plugin::pid())
}
/// Add two numbers.
///
/// The result is their sum.
+8
View File
@@ -49,6 +49,14 @@ impl Plugin {
#[test]
fn discovery_and_dispatch() {
let doc = Plugin.discover();
assert!(doc["components"]["schemas"].get("Empty").is_none());
let choice = doc["methods"]
.as_array()
.unwrap()
.iter()
.find(|m| m["name"] == "choice")
.unwrap();
assert!(choice.get("x-sausage-params").is_none());
assert!(
doc["x-sausage-endpoints"]["control"]["methods"]
.as_array()