Skip to content

feat: Add event subscriptions and CLI sender#158

Merged
doubleailes merged 5 commits into
mainfrom
feat/event-consumer
Apr 29, 2026
Merged

feat: Add event subscriptions and CLI sender#158
doubleailes merged 5 commits into
mainfrom
feat/event-consumer

Conversation

@doubleailes
Copy link
Copy Markdown
Owner

Summary

Tooling + lib changes that make the new RPC and events features testable locally without the RabbitMQ management UI.

Library

  • New EventHandler type alias mirroring RpcHandler: `Arc<dyn Fn(RpcContext, Value) -> BoxFuture<GirolleResult<()>> + Send + Sync>`.
  • `RpcService::subscribe(source_service, event_type, handler)` registers an async event handler. A service may now consist of RPC methods, event subscriptions, or both. The empty-service panic now triggers only when both are empty.
  • New `queue::create_event_consumer_channel` declares the source's `{source}.events` topic exchange (idempotent, durable), declares a durable consumer queue `evt--<event_type>--`, binds with the routing key, and applies `prefetch_count`. The consumer can come up before any publisher.
  • `rpc_service` startup spins up one consumer per subscription. Each delivery is decoded as JSON, wrapped in an `RpcContext` (with the inbound headers stamped on the `RpcCaller`), and handed to the handler. Acks always fire, even if the handler errors — events are best-effort.
  • The shared `Semaphore` is now `Arc` so RPC and event consumers share the same `max_workers` budget.

Examples

  • `cli_sender` — generic CLI RPC sender:
    `cargo run --example cli_sender -- [arg ...]`
    Args are parsed as JSON when possible, falling back to plain strings, so numbers / booleans / objects can be passed directly.
  • `event_observer` — subscribes to `users.user_created` and prints each event as it arrives.

Relationship to other PRs

Local test plan

  1. `cargo run --example simple_macro`
  2. `cargo run --example proxy_service`
  3. `cargo run --example cli_sender -- proxy hello Girolle` → expect `"Hello, Girolle!"`.
  4. (After feat: Wire event dispatcher for ctx.events.dispatch #157) `cargo run --example event_emitter` and `cargo run --example event_observer` in two terminals.
  5. `cargo run --example cli_sender -- users create_user Girolle`
  6. The observer prints `[users.user_created] {"name":"Girolle"}`.

Checks

  • `cargo build --workspace --examples`
  • `cargo test --workspace --lib` (14 passed)
  • `cargo test --workspace --doc` (31 + 1 passed — one new doctest for `subscribe`)
  • `cargo clippy --workspace --all-targets`

Known limitations (deferred)

  • No `#[girolle_event]` macro yet; subscribers construct an `Arc` closure manually. Same shape as the hand-rolled `RpcTask::new` form, so consistent with the rest of the API.
  • One handler per `(source, event_type)` per service. Multiple handlers would need queue-name disambiguation; not hard, can follow up if needed.
  • Event handler errors are logged but don't nack — best-effort delivery.

🤖 Generated with Claude Code

@qodo-code-review
Copy link
Copy Markdown

ⓘ You've reached your Qodo monthly free-tier limit. Reviews pause until next month — upgrade your plan to continue now, or link your paid account if you already have one.

Adds a programmatic event-consumer API and a generic CLI sender to
make local testing of services and events possible without the
RabbitMQ management UI or hand-edited example files.

Library
-------

* New `EventHandler` type alias mirroring `RpcHandler`:
    Arc<dyn Fn(RpcContext, Value) -> BoxFuture<GirolleResult<()>>
        + Send + Sync>
* `RpcService::subscribe(source_service, event_type, handler)` —
  registers an async event handler. A service may now consist of
  RPC methods, event subscriptions, or both. The empty-service
  panic now triggers only when both are empty.
* New `queue::create_event_consumer_channel` declares the source's
  `{source}.events` topic exchange (idempotent, durable), declares
  a durable consumer queue named
  `evt-<source>-<event_type>--<service>`, binds with the routing
  key, and applies `prefetch_count`. The consumer can come up
  before any publisher.
* `rpc_service` startup spins up one consumer per subscription;
  each delivery is decoded as JSON, wrapped in an `RpcContext`
  (with the inbound headers stamped on the RpcCaller), and handed
  to the handler. Acks always fire, even if the handler errors —
  events are best-effort.
* The shared `Semaphore` is now `Arc<Semaphore>` so RPC and event
  consumers share the same `max_workers` budget.

Examples
--------

* `cli_sender` — a generic RPC sender:
    cargo run --example cli_sender -- <service> <method> [arg ...]
  Args are parsed as JSON when possible, falling back to plain
  strings, so numbers/booleans/objects can be sent directly.
* `event_observer` — subscribes to `users.user_created` and prints
  each event payload as it arrives.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@doubleailes doubleailes force-pushed the feat/event-consumer branch from dff6b72 to 0cd94da Compare April 29, 2026 11:17
doubleailes and others added 4 commits April 29, 2026 18:56
Refresh the README, the front-page lib.rs doc that powers the docs.rs
page, and the Zola getting-started chapters to reflect the async
handler rework that landed across the recent feature PRs.

* Replace the outdated `&[Value]`-sync hand-rolled handler snippet with
  the current `Arc<Fn(RpcContext, Payload) -> BoxFuture<...>>` form.
* Document `RpcContext` and the two capability handles it carries:
  `ctx.rpc.call(service, method, payload)` for in-service RPC and
  `ctx.events.dispatch(source, event_type, &payload)` for events.
* Document `RpcService::subscribe(source, event_type, handler)` with
  example code; note that subscriptions and `register(...)` calls can
  coexist, and either alone is valid.
* Drop the stale "no proxy / no pubsub" caveats that describe the
  pre-async era, and mark the corresponding checklist items.
* Index every runnable example (proxy_service, event_emitter,
  event_observer, cli_sender) in a single table in the README and
  quick-start chapter.
* Three new doctests on the front-page lib.rs example all compile.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot <copilot@github.com>
@doubleailes doubleailes merged commit 5c0e737 into main Apr 29, 2026
5 checks passed
@doubleailes doubleailes deleted the feat/event-consumer branch April 29, 2026 17:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant