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.

ThingConventionExample
Config struct{Crate}ConfigAudioConfig, HostConfig, RenderConfig
Plugin struct{Crate}PluginAudioPlugin, RenderPlugin
Stats struct{Crate}StatsAudioStats, RenderStats
Error enum{Crate}ErrorAudioError, AssetError, EngineError
Event enum{Crate}EventAudioEvent, AssetEvent
Command queue{Crate}CommandQueueAudioCommandQueue, UiCommandQueue
System fn{verb}_{noun}_systemaudio_submit_system, audio_sync_stats_system
Test helper{resource}() or setup_{thing}()plugin_world(), setup_world(), voice(idx)
Modulessnake_casecommand_queue, voice_pool, content_hash
ConstantsSCREAMING_SNAKECRASH_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);
}