Coding Conventions
New code should match what's already here. If something looks weird but every crate does it the same way, that's on purpose.
Error Types
Every crate error enum is #[repr(u8)]. One byte. No heap allocation on the error path. Each error type needs code conversion helpers plus a stable label/name for metrics. Test the size and repr round-trip.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum AudioError {
QueueFull = 0,
VoiceLimitReached = 1,
InvalidVoice = 2,
CommandDropped = 3,
CallbackOverrun = 4,
BridgeDisconnected = 5,
InvalidParam = 6,
}
Plugins, Configs, and Stats
Every subsystem is a plugin. Plugin structs hold config, call config.validate() first in build(), insert resources, register systems, and remove the exact same resources in cleanup(). Configs implement Resource, Default, and validate(). Stats resources are plain numeric counters, derive Default, and start at zero.
pub trait Plugin: Send + Sync {
fn build(&self, world: &mut World, schedule: &mut Schedule);
fn name(&self) -> &str;
fn cleanup(&self, _world: &mut World) {}
}
Systems
Use stateless closures where possible. Use explicit System implementations when you need declared component access for parallel scheduling. Resource access goes through world.resource() and world.resource_mut(); queries through world.query(); structural mutation through EcsCommandBuffer. Missing access declarations panic in debug and become data races in release.
lib.rs and Naming
Each crate’s lib.rs starts with a //! doc block covering module overview, architecture, and rules compliance, then exposes public modules. Naming is intentionally uniform across crates.
| Thing | Convention | Example |
|---|---|---|
| Config struct | {Crate}Config | AudioConfig, HostConfig, RenderConfig |
| Plugin struct | {Crate}Plugin | AudioPlugin, RenderPlugin |
| Stats struct | {Crate}Stats | AudioStats, RenderStats |
| Error enum | {Crate}Error | AudioError, AssetError, EngineError |
| Event enum | {Crate}Event | AudioEvent, AssetEvent |
| Command queue | {Crate}CommandQueue | AudioCommandQueue, UiCommandQueue |
| System fn | {verb}_{noun}_system | audio_submit_system, audio_sync_stats_system |
| Test helper | {resource}() or setup_{thing}() | plugin_world(), setup_world(), voice(idx) |
| Modules | snake_case | command_queue, voice_pool, content_hash |
| Constants | SCREAMING_SNAKE | CRASH_LOG_CAPACITY |
Tests, Unsafe, and Cargo
Integration tests go in tests/, unit tests at the bottom of source files. Test helpers should stay short and readable. Every unsafe block gets a real // SAFETY: explanation. Workspace features should follow the common patterns like serde and instrumentation, with default features explicit and optional dependencies isolated properly.
// SAFETY: _mm_prefetch is always safe to call:
// - It's a hint to the CPU, not a memory access
// - Invalid addresses are silently ignored
// - Cannot cause memory faults or undefined behavior
unsafe {
use std::arch::x86_64::_mm_prefetch;
_mm_prefetch(ptr as *const i8, std::arch::x86_64::_MM_HINT_T0);
}