refactor(sdk): consolidate the SDK error/bridge helpers

`http.rs` hand-rolled its own `block_on` + `map_http_err`, and `secrets.rs` /
`email.rs` / `users.rs` each carried a byte-identical `runtime_err` — five
`#[allow(clippy::unnecessary_box_returns)]` across four files for one pattern.

Move `runtime_err` to `bridge.rs` beside `block_on` (single home, single allow)
and have the three bridges import it. `http.rs` now calls the shared
`bridge::block_on("http", …)` (same "http:"-prefixed error, so messages are
unchanged) and its `err` validation helper delegates to `bridge::runtime_err`.
Deletes 4 duplicate fns and 3 of the 5 allows; no behavior change (pinned by
the executor-core SDK contract tests).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-13 19:36:31 +02:00
parent aa3f0531f0
commit f1b480f2eb
5 changed files with 28 additions and 55 deletions

View File

@@ -32,11 +32,10 @@
use std::collections::BTreeMap;
use std::sync::Arc;
use picloud_shared::{HttpError, HttpRequest, HttpResponse, HttpService, SdkCallCx, Services};
use picloud_shared::{HttpRequest, HttpResponse, HttpService, SdkCallCx, Services};
use rhai::{Dynamic, Engine as RhaiEngine, EvalAltResult, Map, Module};
use tokio::runtime::Handle as TokioHandle;
use super::bridge::{dynamic_to_json, json_to_dynamic};
use super::bridge::{block_on, dynamic_to_json, json_to_dynamic, runtime_err};
/// Bridge-side defaults (the service clamps server-side too). The
/// `MAX_*` ceilings stay `i64` because they're compared against the
@@ -280,7 +279,11 @@ fn invoke(
max_redirects: opts.max_redirects,
script_id: Some(cx.script_id.to_string()),
};
let resp = block_on(svc, cx, req)?;
let resp = {
let svc = svc.clone();
let cx = cx.clone();
block_on("http", async move { svc.request(&cx, req).await })?
};
Ok(response_to_dynamic(&resp))
}
@@ -310,7 +313,11 @@ fn invoke_form(
max_redirects: opts.max_redirects,
script_id: Some(cx.script_id.to_string()),
};
let resp = block_on(svc, cx, req)?;
let resp = {
let svc = svc.clone();
let cx = cx.clone();
block_on("http", async move { svc.request(&cx, req).await })?
};
Ok(response_to_dynamic(&resp))
}
@@ -356,36 +363,10 @@ fn dyn_to_string(v: &Dynamic) -> String {
}
}
// Rhai's native-fn error channel is `Box<EvalAltResult>`, so these
// helpers return the boxed form the call sites need.
// A validation error, prefixed like the service's runtime errors (the shared
// `bridge::block_on("http", …)` prefixes the same way, so messages are uniform).
// Delegates to `bridge::runtime_err`; the boxed return is Rhai's error channel.
#[allow(clippy::unnecessary_box_returns)]
fn err(msg: String) -> Box<EvalAltResult> {
EvalAltResult::ErrorRuntime(format!("http: {msg}").into(), rhai::Position::NONE).into()
}
/// Run the async service call from the synchronous Rhai context. Same
/// pattern as `kv`/`docs`: the script runs under `spawn_blocking`, so a
/// runtime handle is reachable and blocking on it is correct.
fn block_on(
svc: &Arc<dyn HttpService>,
cx: &Arc<SdkCallCx>,
req: HttpRequest,
) -> Result<HttpResponse, Box<EvalAltResult>> {
let handle = TokioHandle::try_current().map_err(|e| -> Box<EvalAltResult> {
EvalAltResult::ErrorRuntime(
format!("http: no tokio runtime available: {e}").into(),
rhai::Position::NONE,
)
.into()
})?;
let svc = svc.clone();
let cx = cx.clone();
handle
.block_on(async move { svc.request(&cx, req).await })
.map_err(map_http_err)
}
#[allow(clippy::unnecessary_box_returns)]
fn map_http_err(e: HttpError) -> Box<EvalAltResult> {
EvalAltResult::ErrorRuntime(format!("http: {e}").into(), rhai::Position::NONE).into()
runtime_err(&format!("http: {msg}"))
}