|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Commands |
| 6 | + |
| 7 | +```bash |
| 8 | +# Build and type-check (--all-features requires system deps for openssl; omit locally if Perl unavailable) |
| 9 | +cargo check --all --bins --examples --tests --all-features |
| 10 | + |
| 11 | +# Run all tests (requires a running RabbitMQ instance) |
| 12 | +cargo test |
| 13 | + |
| 14 | +# Run a single test file |
| 15 | +cargo test --test tokio # tokio integration tests |
| 16 | +cargo test --test smol # smol integration tests |
| 17 | +cargo test --test runtime_isolation --features=tokio |
| 18 | + |
| 19 | +# Lint |
| 20 | +cargo clippy --all-features -- -W clippy::all |
| 21 | + |
| 22 | +# Format check |
| 23 | +cargo fmt --all -- --check |
| 24 | + |
| 25 | +# Format (apply) |
| 26 | +cargo fmt --all |
| 27 | + |
| 28 | +# Docs |
| 29 | +RUSTDOCFLAGS=-D warnings cargo doc --no-deps --document-private-items --all-features |
| 30 | + |
| 31 | +# Regenerate protocol code (after changing templates/ or lapin.json) |
| 32 | +./regen-code.sh |
| 33 | +``` |
| 34 | + |
| 35 | +Tests require RabbitMQ running locally (default port 5672). The CI uses a Docker `rabbitmq:latest` service. |
| 36 | + |
| 37 | +## Architecture |
| 38 | + |
| 39 | +Lapin is an async AMQP 0.9.1 client. The core abstraction is a `Connection` (one TCP socket) that multiplexes many lightweight `Channel`s. All I/O runs on a single background thread (the IO loop); user code interacts with it via lock-free channels. |
| 40 | + |
| 41 | +### Key layers |
| 42 | + |
| 43 | +**Connection & Channel (`connection.rs`, `channel.rs`)** |
| 44 | +- `Connection::connect()` / `Connection::connect_with_runtime()` are the entry points. |
| 45 | +- `connection.create_channel()` creates logical channels on the same socket. |
| 46 | +- `channel.rs` (~44 KB, hand-written) implements every AMQP operation (basic_publish, basic_consume, queue_declare, …). It delegates the heavy lifting to the generated layer. |
| 47 | +- `src/generated/channel.rs` (~73 KB, **do not edit by hand**) is produced by the codegen system and contains all option structs and low-level method implementations. |
| 48 | + |
| 49 | +**IO Loop (`io_loop.rs`)** |
| 50 | +- Spawned once per connection as a background thread via `runtime.rs`. |
| 51 | +- Owns the socket, read/write buffers, and the heartbeat timer. |
| 52 | +- Implements automatic reconnection with exponential backoff (`backon` crate). |
| 53 | +- State machine: `Initial → Connected → Stop`. |
| 54 | + |
| 55 | +**Internal RPC (`internal_rpc.rs`)** |
| 56 | +- `InternalRPCHandle` (cloneable `flume` sender) lets channels submit commands (Ack, Nack, Reject, CreateChannel, CloseChannel, …) to the IO loop without shared mutable state. |
| 57 | +- `InternalRPC` (receiver side) processes the command queue inside the IO loop. |
| 58 | + |
| 59 | +**Frame handling (`frames.rs`, `parsing.rs`)** |
| 60 | +- `Frames` manages the outbound frame queue and tracks expected replies. |
| 61 | +- `ExpectedReply` associates an outgoing frame with a promise resolver so callers can `await` the server response. |
| 62 | +- Frame serialization/deserialization is done by the `amq-protocol` crate. |
| 63 | + |
| 64 | +**Promise system (`promise.rs`)** |
| 65 | +- Thin async primitive used instead of `oneshot` channels for RPC-style request/response throughout the library. |
| 66 | +- `Shared<T>` holds the result behind a `Mutex<Option<Result<T>>>` (for ownership transfer on take) and a `Listener` (notify side only) for waking the waiting task. |
| 67 | +- `Promise<T>` holds its own `Listener` (cloned from `Shared` at construction, sharing the same `Arc<Event>`) for the arm/poll side. |
| 68 | +- `poll` uses the arm-then-recheck loop to close the lost-wakeup race window; the resolver drops the data lock before calling `notify()`. |
| 69 | + |
| 70 | +**Listener (`listener.rs`)** |
| 71 | +- `Listener` wraps `event_listener::{Event, EventListener}` and is used by `Promise`, `Consumer`'s stream impl, and `Notifier`. |
| 72 | +- Usage pattern: call `arm()` before checking the guarded condition, `disarm()` if it is met, otherwise `poll()` to register the waker; `notify()` wakes all clones sharing the same `Event`. |
| 73 | + |
| 74 | +**Topology & recovery (`topology.rs`, `channel_recovery_context.rs`)** |
| 75 | +- `Topology` tracks declared exchanges, queues, and bindings so the IO loop can replay them after a reconnect. |
| 76 | +- `ConnectionProperties::enable_auto_recover()` enables this behavior. |
| 77 | + |
| 78 | +### Code generation |
| 79 | + |
| 80 | +Templates live in `templates/channel.rs` (Handlebars) and `templates/lapin.json` (extra metadata). Running `./regen-code.sh` sets `LAPIN_CODEGEN_DIR=src/generated`, invokes `cargo build --features=codegen-internal`, and then formats the output. The generated file is committed; `build.rs` only regenerates it when the `codegen-internal` feature is active. |
| 81 | + |
| 82 | +When the AMQP spec or method signatures need to change, edit `templates/` and re-run `./regen-code.sh`, then commit both. |
| 83 | + |
| 84 | +### Feature flags |
| 85 | + |
| 86 | +| Category | Flags | |
| 87 | +|----------|-------| |
| 88 | +| Runtime (pick one) | `tokio` (default), `smol`, `async-global-executor` | |
| 89 | +| TLS | `rustls` (default), `native-tls`, `openssl`, `rustls-platform-verifier`, `rustls-native-certs`, `rustls-webpki-roots-certs` | |
| 90 | +| Rustls crypto | `rustls--aws_lc_rs` (default), `rustls--ring` (more portable) | |
| 91 | +| DNS | `hickory-dns` | |
| 92 | +| Codegen | `codegen` (user-facing), `codegen-internal` (build.rs only) | |
| 93 | + |
| 94 | +MSRV is **1.88.0** (Rust 2024 edition). |
0 commit comments