Skip to main content

toolkit/
wiring.rs

1//! Runtime helpers consumed by `#[toolkit::provides]`.
2//!
3//! Kept separate from the proc-macro crate so the generated code references
4//! stable, version-controlled host APIs rather than re-importing them from
5//! `toolkit_contract`. Provider gears don't import these directly — the
6//! macro emits paths like `::toolkit::wiring::read_wiring(...)`.
7
8use std::sync::Arc;
9
10use toolkit_contract::policy::{PolicyStack, TracingPolicy};
11use toolkit_contract::wiring::ClientWiring;
12
13use crate::context::GearCtx;
14
15/// Read the [`ClientWiring`] for a single provided contract from the
16/// module's config section.
17///
18/// Path: `gears.<gear>.config.client_wiring.<key>`. If `client_wiring`
19/// or the contract `key` is absent, returns [`ClientWiring::Local`] —
20/// gears whose only provided contract has a local-only deployment can
21/// run with no config at all.
22///
23/// `key` is the `snake_case` form of the contract trait identifier (e.g.
24/// `payment_api` for `PaymentApi`); the macro takes care of casing.
25///
26/// # Errors
27/// Returns a context-bearing `anyhow::Error` if `client_wiring.<key>` is
28/// present but cannot be deserialized into a [`ClientWiring`].
29pub fn read_wiring(ctx: &GearCtx, key: &str) -> anyhow::Result<ClientWiring> {
30    let raw = ctx.raw_config();
31    let Some(section) = raw.get("client_wiring") else {
32        return Ok(ClientWiring::Local);
33    };
34    let Some(per_contract) = section.get(key) else {
35        return Ok(ClientWiring::Local);
36    };
37    serde_json::from_value::<ClientWiring>(per_contract.clone()).map_err(|e| {
38        anyhow::anyhow!(
39            "gear `{gear}`: invalid client_wiring.{key}: {e}",
40            gear = ctx.gear_name()
41        )
42    })
43}
44
45/// Default [`PolicyStack`] applied to in-process local clients built by
46/// `#[toolkit::provides]`. Contains [`TracingPolicy`] only; richer stacks
47/// can be opted into via the `policies = [...]` macro argument.
48#[must_use]
49pub fn default_policy_stack() -> Arc<PolicyStack> {
50    let mut s = PolicyStack::new();
51    s.push(Arc::new(TracingPolicy));
52    Arc::new(s)
53}
54
55/// Build a [`PolicyStack`] from a list of already-constructed policy
56/// instances (the macro emits `Box::new(Policy::default())` per entry
57/// when the user passes `policies = [...]`).
58#[must_use]
59pub fn policy_stack_from(
60    policies: Vec<Arc<dyn toolkit_contract::policy::Policy>>,
61) -> Arc<PolicyStack> {
62    let mut s = PolicyStack::new();
63    for p in policies {
64        s.push(p);
65    }
66    Arc::new(s)
67}
68
69#[cfg(test)]
70#[cfg_attr(coverage_nightly, coverage(off))]
71#[path = "wiring_tests.rs"]
72mod tests;