From fb2a8e0a80f3609e4d65aad448cd057b9481a64f Mon Sep 17 00:00:00 2001 From: Edison Jwa Date: Fri, 5 Jun 2026 11:54:59 +0900 Subject: [PATCH] docs(rust): add doc comments to delta enums and fix dead_code warnings (#24) * docs(rust): add doc comments to delta enums and fix dead_code warnings Add missing documentation to ProtocolDelta, CoreDelta, and BridgeDelta enum variants and their struct fields across the protocol, core, and bridge crates. Document the ChannelId::ROOT constant and the take_delta_rx adapter method. Fix dead_code warnings: - keyring_disabled: add #[cfg] gate matching its callers - snapshot_signature: add #[cfg(test)] for future test use * fix(rust): correct `order` field docs to predecessor channel ID, narrow audio engine cfg gates - Correct `order` field documentation in ProtocolDelta, SessionEvent, and BridgeEvent from 'sort order' to 'predecessor channel ID (TeamSpeak linked-list ordering hint)' per Copilot review feedback. - Narrow AudioEngine voice_out_tx, voice_activity_selector, and mic_gain cfg gates from ios+macos+android to android-only, since these fields are only read from self in android_restart_voice_unit. On iOS/macOS the values are passed directly to the voice backend at construction time. --- core/chanora_core/src/lib.rs | 39 ++++++++++++++++++++++++ crates/chanora_audio/src/engine.rs | 9 ++---- crates/chanora_bridge/src/api.rs | 38 ++++++++++++++++++++++++ crates/chanora_protocol/src/adapter.rs | 3 ++ crates/chanora_protocol/src/dto.rs | 41 ++++++++++++++++++++++++++ crates/chanora_storage/src/lib.rs | 6 ++++ 6 files changed, 130 insertions(+), 6 deletions(-) diff --git a/core/chanora_core/src/lib.rs b/core/chanora_core/src/lib.rs index 8c78aba..be5a991 100644 --- a/core/chanora_core/src/lib.rs +++ b/core/chanora_core/src/lib.rs @@ -253,49 +253,87 @@ pub enum SessionEvent { }, /// Audio route changed (speaker/earpiece/BT/wired headset). AudioRouteChanged { + /// New audio output route. route: AudioRoute, }, + /// A client moved to a different channel. ClientMoved { + /// Unique client identifier. client_id: u64, + /// Destination channel. new_channel_id: u64, }, + /// A new client connected. ClientJoined { + /// Unique client identifier. client_id: u64, + /// Channel the client joined. channel_id: u64, + /// Display nickname. name: String, + /// Microphone muted state. input_muted: bool, + /// Speaker muted state. output_muted: bool, + /// Whether this is a server query (bot) client. is_server_query: bool, + /// Client's talk power value. talk_power: i32, + /// Whether the server granted temporary talk power. talk_power_granted: bool, }, + /// A client disconnected. ClientLeft { + /// Unique client identifier. client_id: u64, + /// Display nickname at time of disconnect. name: String, }, + /// Client properties changed. ClientUpdated { + /// Unique client identifier. client_id: u64, + /// Microphone muted state. input_muted: bool, + /// Speaker muted state. output_muted: bool, + /// Whether this is a server query (bot) client. is_server_query: bool, + /// Client's talk power value. talk_power: i32, + /// Whether the server granted temporary talk power. talk_power_granted: bool, }, + /// A new channel appeared. ChannelAdded { + /// Unique channel identifier. id: u64, + /// Parent channel ID. parent: u64, + /// Channel name. name: String, + /// Predecessor channel ID within the same parent (TeamSpeak + /// linked-list ordering hint). Zero means first child. order: i64, + /// Whether the channel requires a password. has_password: bool, + /// Talk power required to speak, or `None` when unrestricted. needed_talk_power: Option, }, + /// A channel was deleted. ChannelRemoved { + /// Channel identifier. id: u64, }, + /// Channel properties changed. ChannelUpdated { + /// Unique channel identifier. id: u64, + /// Channel name. name: String, + /// Whether the channel requires a password. has_password: bool, + /// Talk power required to speak, or `None` when unrestricted. needed_talk_power: Option, }, } @@ -2399,6 +2437,7 @@ async fn supervisor_loop(ctx: SupervisorContext) { /// the UI would render. Two snapshots with identical channel /// memberships, names, and orderings produce the same signature; /// any in-channel move, rename, or reorder produces a different one. +#[cfg(test)] fn snapshot_signature(snap: &ServerSnapshot) -> u64 { use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; diff --git a/crates/chanora_audio/src/engine.rs b/crates/chanora_audio/src/engine.rs index ce32975..176bb83 100644 --- a/crates/chanora_audio/src/engine.rs +++ b/crates/chanora_audio/src/engine.rs @@ -306,11 +306,11 @@ pub struct AudioEngine { audio_processing_config: Arc>, audio_processing_stats: Arc, audio_handler: Arc>>, - #[cfg(any(target_os = "ios", target_os = "macos", target_os = "android"))] + #[cfg(target_os = "android")] voice_out_tx: mpsc::Sender, - #[cfg(any(target_os = "ios", target_os = "macos", target_os = "android"))] + #[cfg(target_os = "android")] voice_activity_selector: Option>, - #[cfg(any(target_os = "ios", target_os = "macos", target_os = "android"))] + #[cfg(target_os = "android")] mic_gain: f32, // Streams must be dropped to stop audio. Both are `!Send` because // cpal's Stream isn't Send on some backends; we keep them in an @@ -1308,9 +1308,6 @@ impl AudioEngine { audio_processing_config, audio_processing_stats, audio_handler, - voice_out_tx, - voice_activity_selector: cfg.voice_activity_selector.clone(), - mic_gain: cfg.mic_gain, _ios_voice_backend: Mutex::new(Some(ios_voice_backend)), shutdown_tx: Some(shutdown_tx), capture_active, diff --git a/crates/chanora_bridge/src/api.rs b/crates/chanora_bridge/src/api.rs index a57ec69..8aa4a2c 100644 --- a/crates/chanora_bridge/src/api.rs +++ b/crates/chanora_bridge/src/api.rs @@ -1701,49 +1701,87 @@ pub enum BridgeEvent { }, /// Audio route changed (speaker/earpiece/BT/wired). AudioRouteChanged { + /// New audio output route. route: BridgeAudioRoute, }, + /// A client moved to a different channel. ClientMoved { + /// Unique client identifier. client_id: u64, + /// Destination channel. new_channel_id: u64, }, + /// A new client connected. ClientJoined { + /// Unique client identifier. client_id: u64, + /// Channel the client joined. channel_id: u64, + /// Display nickname. name: String, + /// Microphone muted state. input_muted: bool, + /// Speaker muted state. output_muted: bool, + /// True for server query (bot) clients. is_server_query: bool, + /// Client's talk power value. talk_power: i32, + /// Whether the server granted temporary talk power. talk_power_granted: bool, }, + /// A client disconnected. ClientLeft { + /// Unique client identifier. client_id: u64, + /// Display nickname at time of disconnect. name: String, }, + /// Client properties changed. ClientUpdated { + /// Unique client identifier. client_id: u64, + /// Microphone muted state. input_muted: bool, + /// Speaker muted state. output_muted: bool, + /// True for server query (bot) clients. is_server_query: bool, + /// Client's talk power value. talk_power: i32, + /// Whether the server granted temporary talk power. talk_power_granted: bool, }, + /// A new channel appeared. ChannelAdded { + /// Unique channel identifier. id: u64, + /// Parent channel ID. parent: u64, + /// Channel name. name: String, + /// Predecessor channel ID within the same parent (TeamSpeak + /// linked-list ordering hint). Zero means first child. order: i64, + /// Whether the channel requires a password. has_password: bool, + /// Talk power required to speak; `None` means no restriction. needed_talk_power: Option, }, + /// A channel was deleted. ChannelRemoved { + /// Channel identifier. id: u64, }, + /// Channel properties changed. ChannelUpdated { + /// Unique channel identifier. id: u64, + /// Channel name. name: String, + /// Whether the channel requires a password. has_password: bool, + /// Talk power required to speak; `None` means no restriction. needed_talk_power: Option, }, } diff --git a/crates/chanora_protocol/src/adapter.rs b/crates/chanora_protocol/src/adapter.rs index c99c000..e707ae1 100644 --- a/crates/chanora_protocol/src/adapter.rs +++ b/crates/chanora_protocol/src/adapter.rs @@ -471,6 +471,9 @@ impl ProtocolClient { } } + /// Takes ownership of the delta receiver channel. Returns `None` if already + /// taken. Must be called exactly once during initialization to subscribe to + /// incremental state changes. pub fn take_delta_rx(&self) -> Option> { self.delta_rx.lock().ok().and_then(|mut g| g.take()) } diff --git a/crates/chanora_protocol/src/dto.rs b/crates/chanora_protocol/src/dto.rs index ca1c73b..fdf076b 100644 --- a/crates/chanora_protocol/src/dto.rs +++ b/crates/chanora_protocol/src/dto.rs @@ -168,52 +168,93 @@ pub struct ServerSnapshot { } impl ChannelId { + /// Root/top-level channel identifier. pub const ROOT: ChannelId = ChannelId(0); } +/// Incremental state change emitted by the protocol adapter when the server tree changes. +/// +/// Covers client joins, leaves, moves, and channel additions, removals, and updates. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum ProtocolDelta { + /// A client moved to a different channel. ClientMoved { + /// Unique client identifier. client_id: u64, + /// Destination channel identifier. new_channel_id: u64, }, + /// A new client appeared on the server. ClientJoined { + /// Unique client identifier. client_id: u64, + /// Channel the client joined. channel_id: u64, + /// Display nickname. name: String, + /// Microphone muted state. input_muted: bool, + /// Speaker muted state. output_muted: bool, + /// Whether the client is a server query client. is_server_query: bool, + /// Client's talk power value. talk_power: i32, + /// Whether the server granted temporary talk power. talk_power_granted: bool, }, + /// A client disconnected. ClientLeft { + /// Unique client identifier. client_id: u64, + /// Display nickname. name: String, }, + /// A client's properties changed. ClientUpdated { + /// Unique client identifier. client_id: u64, + /// Microphone muted state. input_muted: bool, + /// Speaker muted state. output_muted: bool, + /// Whether the client is a server query client. is_server_query: bool, + /// Client's talk power value. talk_power: i32, + /// Whether the server granted temporary talk power. talk_power_granted: bool, }, + /// A new channel appeared. ChannelAdded { + /// Channel identifier. id: u64, + /// Parent channel identifier. parent: u64, + /// Channel name. name: String, + /// Predecessor channel ID within the same parent (TeamSpeak + /// linked-list ordering hint). Zero means first child. order: i64, + /// Whether the channel has a password. has_password: bool, + /// Needed talk power, or `None` when unrestricted. needed_talk_power: Option, }, + /// A channel was deleted. ChannelRemoved { + /// Channel identifier. id: u64, }, + /// Channel properties changed. ChannelUpdated { + /// Channel identifier. id: u64, + /// Channel name. name: String, + /// Whether the channel has a password. has_password: bool, + /// Needed talk power, or `None` when unrestricted. needed_talk_power: Option, }, } diff --git a/crates/chanora_storage/src/lib.rs b/crates/chanora_storage/src/lib.rs index d79f915..f91a2dd 100644 --- a/crates/chanora_storage/src/lib.rs +++ b/crates/chanora_storage/src/lib.rs @@ -628,6 +628,12 @@ fn read_file_dek(path: &Path) -> Result<[u8; 32], StorageError> { /// headless / sandboxed environments force the file-fallback path /// without poking a real OS keyring (which would either prompt the /// user or block on a missing D-Bus session). +#[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "windows", + target_os = "ios" +))] fn keyring_disabled() -> bool { matches!( std::env::var("CHANORA_DISABLE_KEYRING").as_deref(),