Files
rutster/LEARNING.md
adlee-was-taken f765fc2b27 docs: LEARNING.md + fuzz/ placeholder + README dev-loop (spec §7, §6.3)
LEARNING.md indexes ten concept-to-file pointers (the spec floor was
five) — the newtype pattern, exhaustive enum match, sans-IO, trait
extension seams, thiserror + hot-path match-and-continue, Arc<Mutex>
vs Arc<RwLock>, DashMap, str0m's single-mutation invariant, graceful
shutdown, include_str!. fuzz/README.md pre-paves the layout (no
hostile-bytes surface in slice 1; harnesses land at step 5 per the
out-of-scope table). README's new dev-loop section documents the
libopus FFI prerequisite and the manual e2e steps.
2026-06-28 13:06:37 -04:00

75 lines
3.7 KiB
Markdown

# LEARNING.md — to learn concept X, read file Y
This index maps a Rust concept you might be learning to the file where
slice 1 makes the concept concrete. Each entry is a worked example you
can read in `cargo doc --open` plus the source file itself.
## Concepts + pointers
- **Newtype pattern (type-safety via single-field wrappers)** →
`crates/rutster-call-model/src/lib.rs``ChannelId(Uuid)`. The newtype
stops us from mixing up a `ChannelId` with some future `SessionId` at
the type-system level. Compile-enforced where a comment could only ask.
- **`enum` for closed state sets + exhaustive `match`** →
`crates/rutster-call-model/src/lib.rs``ChannelState` (New →
Connecting → Connected → Closing → Closed). Exhaustiveness checking
forces every `match` to consider each state; adding a state later
surfaces every site that needs handling.
- **Sans-IO pattern (no I/O inside the library; input via method calls,
output via return values)** → `crates/rutster-media/src/loop_driver.rs`
— the str0m poll loop. `Rtc::handle_input` takes a network packet as a
struct argument, not from a socket the library owns; `poll_output`
returns `Transmit` packets the caller sends. Fully testable without a
network — str0m integration tests use this property to drive faster
than realtime.
- **Trait design for extension points (a futures-compatible seam)** →
`crates/rutster-media/src/pcm.rs` — the `AudioSource` / `AudioSink`
traits. Slice 1 wires an `EchoAudioPipe` between them; step 2 swaps
that for a real WSS tap client without touching `RtcSession`. The
traits describe *what* the splice point does, not *how* it's filled.
- **Error enums with `thiserror` + hot-path match-and-continue** →
`crates/rutster-media/src/lib.rs` (`MediaError`) and
`crates/rutster-media/src/opus_codec.rs` (`OpusDecoder::decode` returns
`Option<PcmFrame>`). Cold path: `thiserror`-derived enum + `?`. Hot
path: match-and-continue, never `?`, never panic — "drop + observe,
don't crash" (spec §3.8).
- **`Arc<Mutex<T>>` vs `Arc<RwLock<T>>` — when each is right** →
`crates/rutster/src/session_map.rs`. The `RtcSession` lives behind
`Arc<Mutex<...>>` because every access mutates it (str0m's `&mut self`
contract) — `RwLock`'s read-mode would be useless. Comment on the
struct explains the trade-off.
- **`DashMap` for sharded concurrent maps** →
`crates/rutster/src/session_map.rs`. `DashMap` shards its inner map so
two handlers operating on different `ChannelId`s don't contend;
`HashMap` wrapped in a single `Mutex` would serialize every access.
- **str0m 0.21's single-mutation invariant** →
`crates/rutster-media/src/loop_driver.rs`. Mutate (handle_input /
Writer::write) → drain `poll_output` to `Output::Timeout` → next
mutate. Violating this leaves str0m in an inconsistent state.
- **tokio graceful shutdown via signal handlers** →
`crates/rutster/src/main.rs` (`shutdown_signal`). Ctrl-C / SIGTERM
drops the AppState; the AppState drops the DashMap; the DashMap drops
every RtcSession. No in-flight call preservation in slice 1.
- **`include_str!` for embedding static assets** →
`crates/rutster/src/routes.rs` (`include_str!("../static/index.html")`).
The HTML test client is compiled into the binary at build time — no
separate file to ship, no disk IO to serve it.
## How to read
1. `cargo doc --open` — every module has a `//!` doc comment; the doc
tree is the high-level map.
2. Pick a concept above; open the named file. The first occurrence of
each non-obvious pattern has a `//` comment explaining *why*.
3. Cross-ref back to the spec sections cited inline (`spec §3.8`,
`ADR-0002`, etc.) for the architecture-level rationale.