- Rust 100%
Replace specific git URLs with comments directing users to use configuration management for dependency references. |
||
|---|---|---|
| crates | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| README.md | ||
hero_macros_previous
Frozen, known-good ("previous") snapshot of the macro + OpenRPC client
stack that main across the hero repositories already builds against.
Why this repo exists
The active refactor moved the macro stack onto a fast-moving new home
(hero_lib for the new derive macros, hero_blueprint for the scaffolder).
Every service repo was therefore forced to pin a moving target — typically
hero_blueprint (or the older hero_rpc) on branch = development — which
breaks main whenever that branch advances.
This repo freezes the previous, stable macros in one place so that:
mainpins this repo and stops trackinghero_blueprint/hero_lib.mainbecomes buildable and stable on its own again, with nohero_blueprintdependency.maincan then be folded back intodevelopment(development becomes a superset of main).- Each service repo migrates onto the new
hero_liblibrary one at a time, on its own schedule, without breakingmainin the meantime.
The newer, evolving macros stay in hero_lib / hero_blueprint. "New" in the
task ("use these new macros") means the macros now living in this new repo,
not a newer version.
What's here
Two frozen macro stacks live side by side: the old hero_rpc stack (lifted
verbatim from hero_blueprint@f6509f9, the pre-teardown main/integration
tip) and the frozen new-gen derive macro (cut from hero_lib@9657556a).
Workspace version 0.6.0, Rust 1.96:
| crate (path) | package | stack | purpose |
|---|---|---|---|
crates/derive |
hero_rpc_derive |
old | openrpc_client!, openrpc_proxy!, rhai_openrpc! proc macros (emit ::hero_rpc2:: transport) |
crates/hero_rpc2 |
hero_rpc2 |
old | JSON-RPC 2.0 runtime over UDS; owns the OpenRPC transport (folds in the retired hero_rpc_openrpc per hero_rpc#146) |
crates/openrpc |
hero_rpc_openrpc |
old | Deprecated re-export shim; kept so older consumers still resolve |
crates/openrpc_http_client_lib |
hero_rpc_client |
old | Cross-platform OpenRPC HTTP client (native + wasm) |
crates/hero_derive |
hero_derive |
new-gen | Frozen herolib_derive (renamed); openrpc_client! here emits ::herolib_openrpc:: transport. New-stack repos alias herolib_derive = { package = "hero_derive", … } |
crates/herolib_oschema |
herolib_oschema |
new-gen | Host-time OSIS schema dep of hero_derive (vendored, was an external hero_lib dep) |
The scaffolder crates (generator, blueprinter, osis) are intentionally
not included — they are the "too new" parts being decoupled from.
Both
openrpc_client!macros (oldhero_rpc_deriveand new-genhero_derive) carry the same socket-placeholder fix — see Socket placeholder inservers[].urlbelow.
Dependencies — what this repo pulls, and for what
No external git dependencies. Every crate builds against only crates.io
packages plus the workspace-internal path crates below — nothing points at a
still-moving hero_lib / hero_blueprint, so this repo cannot break when those
advance. Cargo.lock is committed for reproducible, frozen builds.
Internal (path) crates — all path = "crates/…", re-exported via
[workspace.dependencies]: hero_rpc_derive, hero_rpc2, hero_rpc_openrpc,
hero_rpc_client, hero_derive, and herolib_oschema (the OSIS schema lib,
vendored here — formerly an external hero_lib dep).
External (crates.io) deps, by crate and purpose:
| crate | external deps | for what |
|---|---|---|
hero_rpc_derive |
proc-macro2, quote, syn, serde, serde_json, prettyplease |
proc-macro plumbing, OpenRPC-spec parsing, pretty-printing the generated client |
hero_rpc2 |
jsonrpsee, tokio, serde(_json), thiserror, tracing, futures; optional schemars, hyper, hyper-util, http-body-util, reqwest, futures-util, syn, prettyplease |
JSON-RPC 2.0 runtime over UDS; OpenRPC transport/codegen gated behind features |
hero_rpc_openrpc |
serde_json |
deprecated shim re-exporting hero_rpc2 |
hero_rpc_client |
serde(_json); wasm: gloo-net, wasm-bindgen, web-sys, js-sys |
cross-platform OpenRPC HTTP client (native uses internal hero_rpc2) |
hero_derive |
proc-macro2, quote, syn, serde(_json), toml, prettyplease |
new-gen derive macros (ToSchema / Otoml / …) |
herolib_oschema |
serde(_json), toml, thiserror, regex; native: fs2 |
OSIS types, OTOML serialization, OsisObject, filesystem collection storage |
The one soft coupling: herolib_core (consumer-side only)
herolib_core is not a build dependency of any crate here. The
openrpc_client! macros only emit a call to
::herolib_core::base::resolve_socket_path_with_override(...) inside the
generated connect() (see the socket-placeholder section above). The
consuming repo must therefore have herolib_core on its dependency path — but
this snapshot itself never compiles against it, keeping the freeze intact.
How consumers use it
Repoint the old dependency, e.g.:
# before — moving target on hero_blueprint / hero_rpc
hero_rpc_derive = { git = "https://forge.ourworld.tf/lhumina_code/hero_blueprint.git", branch = "development" }
hero_rpc2 = { git = "https://forge.ourworld.tf/lhumina_code/hero_blueprint.git", branch = "development", features = ["client", "uds-http", "openrpc-transport"] }
# after — frozen, stable
# [dependency references removed - stable references should be updated via configuration management]
The macro and its runtime are a matched pair — take BOTH from here
openrpc_client! / openrpc_proxy! do not contain a transport; they emit
code that calls into a runtime crate. The two must come from the same frozen
snapshot, or the generated code won't compile / will drift:
| You use this macro… | …it emits calls into | take the runtime from |
|---|---|---|
hero_rpc_derive (old stack) |
::hero_rpc2:: |
hero_rpc2 here |
hero_derive (new-gen, aliased as herolib_derive) |
::herolib_openrpc:: |
see note below |
⚠️ Do NOT pull the OpenRPC runtime from
hero_lib(herolib_openrpconbranch = "development"). That crate moves independently of this frozen macro, so pairing a frozen macro with a liveherolib_openrpcreintroduces exactly the drift this repo exists to stop. The old stack avoids the problem entirely:hero_rpc_deriveemits::hero_rpc2::, andhero_rpc2is fully self-contained and lives here — noherolib_openrpcanywhere.
Recommended for client repos: the old stack. Alias it so existing
herolib_macros::openrpc_client! / openrpc_proxy! call sites need no change,
and add the matching runtime:
# proc-macro — aliased onto the existing `herolib_macros` name
# herolib_macros dependency reference removed - use configuration management instead
# runtime the macro emits into — MUST also come from here, NOT hero_lib
# hero_rpc2 dependency reference removed - use configuration management instead
Then drop any herolib_openrpc = { … hero_lib … } dependency and re-export the
error type from the new runtime instead:
// before
pub use herolib_openrpc::OpenRpcError as MyApiError;
// after
pub use hero_rpc2::OpenRpcError as MyApiError;
hero_rpc2 provides the same surface the generated client needs —
OpenRpcError, RpcError, OpenRpcTransport (::unix_socket), and SseEvent
(with .json()).
The
hero_derive(new-gen) path still emits::herolib_openrpc::, but this repo does not vendor aherolib_openrpccrate — so that path currently has no frozen runtime here. Prefer the old stack above until a frozenherolib_openrpcis vendored.
Socket placeholder in servers[].url (must read before migrating)
openrpc_client! generates a default connect() from the spec's
servers[0].url. When that URL embeds a socket-dir env-var placeholder, the
macro strips unix://, takes the relative tail (<svc>/rpc.sock), and resolves
the real path at runtime via
herolib_core::base::resolve_socket_path_with_override, which applies the
canonical cascade HERO_SOCKET_DIR → PATH_VAR → PATH_ROOT/var/sockets.
The frozen snapshot originally only recognised the old placeholder name
${HERO_SOCKET_DIR}. The current Hero stack renamed it to ${PATH_SOCKET}
(and some specs use other ${...} names). With the old check, any spec using
the new name fell through to the literal-URL branch and emitted a broken
connect() containing the unexpanded placeholder — e.g.
"unix://${PATH_SOCKET}/<svc>/rpc.sock" — so every connect() failed at
runtime with "socket not found at '${PATH_SOCKET}/...'".
Fix applied here (both hero_rpc_derive and hero_derive): the macro now
accepts any ${...} placeholder as the socket-dir segment and always routes
through resolve_socket_path_with_override, regardless of the literal name. So
${HERO_SOCKET_DIR}, ${PATH_SOCKET}, etc. all resolve correctly — this keeps
the backwards-compat macros in line with current sockets.
Migration checklist when repointing a repo at this snapshot:
- No spec change is required — your
servers[].urlmay use either${HERO_SOCKET_DIR}or${PATH_SOCKET}; both now work. - The consuming crate must depend on
herolib_core(the macro emits a call intoherolib_core::base::resolve_socket_path_with_override). - If
connect()still can't find the socket, check that the server actually binds under the resolved dir — setHERO_SOCKET_DIR/PATH_SOCKET/PATH_ROOTconsistently for both server and client, or pass anoverride_env = "..."toopenrpc_client!and export that var.
Build / test
cargo build --workspace
cargo test --workspace --all-features
Both are green. One carried-over SSE test is quarantined — see
crates/derive/tests/README.md.