refactor(qconnect): extract QconnectServiceState to service.rs

The final extraction of Alcance A. Move the public service facade
(QconnectServiceState struct + impl + Default impl, plus the spawned
event-loop task, the renderer/queue snapshot queries, the controller-
handoff helpers, send_command/send_renderer_report, the deferred
renderer-join plumbing, and the bootstrap_remote_presence pending-
action machinery) from mod.rs into a dedicated service submodule.

Items moved:
- struct QconnectRuntime, QconnectServiceInner
- pub struct QconnectServiceState (re-exported via `pub use`)
- impl QconnectServiceState (~50 methods) and impl Default
- bootstrap_remote_presence, deferred_renderer_join,
  clear_pending_if_matches
- queue_payload_track_preview, update_lifecycle_state_if_running,
  emit_qconnect_diagnostic (private helpers used only by the service)
- Constants: JOIN_SESSION_REASON_CONTROLLER_REQUEST,
  QCONNECT_PLAY_TRACK_HANDOFF_WAIT_MS,
  QCONNECT_PLAY_TRACK_HANDOFF_POLL_MS

All methods on QconnectServiceState are pub(super) so the commands
submodule (which was already extracted) can dispatch through them.
The Default impl uses plain `fn default()` (visibility qualifiers are
not permitted in trait impls).

mod.rs now contains only the cross-submodule glue (~140 lines):
shared constants (PLAYING_STATE_*, AUDIO_QUALITY_*, BUFFER_STATE_OK,
DEFAULT_QCONNECT_CHANNEL_COUNT, QCONNECT_REMOTE_QUEUE_SOURCE), the
QconnectRemoteSyncState accumulator, qconnect_now_ms,
qconnect_repeat_mode_from_loop_mode, normalize_volume_to_fraction,
model_track_to_core_queue_track, and the module declarations.

session.rs and commands.rs imports re-pathed to point at submodules
directly (super::transport::*, super::queue_resolution::*,
super::session::*, super::service::*) instead of going through
re-exports in mod.rs.

mod.rs: 1820 → 142 lines (-92%). service.rs: 1689 lines.

Final layout:

  src-tauri/src/qconnect/
  ├── mod.rs              ( 142)  module glue
  ├── types.rs            ( 273)  wire types, payloads
  ├── session.rs          ( 374)  topology, renderer snapshots
  ├── queue_resolution.rs ( 481)  cursors, shuffle order
  ├── corebridge.rs       ( 566)  apply_renderer_command, materialize
  ├── track_loading.rs    ( 337)  streaming, FLAC probe, dedup
  ├── event_sink.rs       ( 461)  QconnectEventSink + dispatch
  ├── transport.rs        ( 386)  credentials, device identity
  ├── service.rs          (1689)  QconnectServiceState facade
  ├── commands.rs         ( 849)  Tauri invoke handlers
  └── tests.rs            (1044)  37 unit tests

From 6371 lines in 1 monolith → 6602 lines across 11 focused files,
each with a single clear responsibility. mod.rs went 6371 → 142 lines
(-98%). All 37 tests pass. Alcance A complete; the codebase is ready
for unified-binary Phase 0 to resume.

cc
This commit is contained in:
vicrodh committed 2026-04-27 18:00:43 -06:00
1 parent 49f56d563e
commit 532ec89095
5 files changed
+1730 -1720

No files matched your search

+10 -9
View File
@@ -15,21 +15,22 @@ use crate::core_bridge::CoreBridgeState;
use crate::runtime::{CommandRequirement, RuntimeError, RuntimeManagerState};
use crate::AppState;
use super::session::QconnectFileAudioQualitySnapshot;
use super::service::QconnectServiceState;
use super::session::{QconnectFileAudioQualitySnapshot, QconnectRendererReportDebugEvent};
use super::transport::{
persist_device_name, resolve_default_qconnect_device_name, resolve_system_hostname,
resolve_transport_config,
};
use super::{
QconnectAdmissionBlockedEvent, QconnectAdmissionResult, QconnectAskForRendererStateRequest,
QconnectConnectOptions, QconnectConnectionStatus, QconnectJoinSessionRequest,
QconnectMuteVolumeRequest, QconnectQueueVersionPayload, QconnectRendererReportDebugEvent,
QconnectHandoffIntent, QconnectSendCommandRequest, QconnectSendCommandWithAdmissionRequest,
QconnectServiceState, QconnectSessionState, QconnectSetActiveRendererRequest,
QconnectSetLoopModeRequest, QconnectSetMaxAudioQualityRequest, QconnectSetPlayerStateRequest,
QconnectSetVolumeRequest, QconnectTrackOrigin, AUDIO_QUALITY_CD, AUDIO_QUALITY_HIRES_LEVEL1,
AUDIO_QUALITY_HIRES_LEVEL2, AUDIO_QUALITY_HIRES_LEVEL3, AUDIO_QUALITY_MP3,
AUDIO_QUALITY_UNKNOWN, BUFFER_STATE_OK, DEFAULT_QCONNECT_CHANNEL_COUNT,
QconnectConnectOptions, QconnectConnectionStatus, QconnectHandoffIntent,
QconnectJoinSessionRequest, QconnectMuteVolumeRequest, QconnectQueueVersionPayload,
QconnectSendCommandRequest, QconnectSendCommandWithAdmissionRequest, QconnectSessionState,
QconnectSetActiveRendererRequest, QconnectSetLoopModeRequest,
QconnectSetMaxAudioQualityRequest, QconnectSetPlayerStateRequest, QconnectSetVolumeRequest,
QconnectTrackOrigin, AUDIO_QUALITY_CD, AUDIO_QUALITY_HIRES_LEVEL1, AUDIO_QUALITY_HIRES_LEVEL2,
AUDIO_QUALITY_HIRES_LEVEL3, AUDIO_QUALITY_MP3, AUDIO_QUALITY_UNKNOWN, BUFFER_STATE_OK,
DEFAULT_QCONNECT_CHANNEL_COUNT,
};
pub(super) fn classify_qconnect_audio_quality(sample_rate: u32, bit_depth: u32) -> i32 {
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+3 -3
View File
@@ -13,9 +13,9 @@ use super::queue_resolution::{
find_cursor_index_by_queue_item_id, normalize_current_queue_item_id_from_queue_state,
ordered_queue_cursors, QconnectOrderedQueueCursor,
};
use super::transport::{default_qconnect_device_info, resolve_qconnect_device_uuid};
use super::{
default_qconnect_device_info, resolve_qconnect_device_uuid, QconnectQueueVersionPayload,
QconnectRemoteSyncState, QconnectVisibleQueueProjection,
QconnectQueueVersionPayload, QconnectRemoteSyncState, QconnectVisibleQueueProjection,
};
pub(super) fn queue_item_snapshot_for_cursor(
@@ -290,7 +290,7 @@ pub(super) fn build_visible_queue_projection(
queue: &QConnectQueueState,
renderer: &QConnectRendererState,
) -> QconnectVisibleQueueProjection {
use super::find_cursor_index_by_track_id;
use super::queue_resolution::find_cursor_index_by_track_id;
let cursors = ordered_queue_cursors(queue);
+5 -5
View File
@@ -100,7 +100,7 @@ fn maps_qconnect_track_origin_to_core_origin_and_handoff() {
#[test]
fn refreshes_local_renderer_id_from_exact_device_uuid_match() {
let local_device_uuid = super::resolve_qconnect_device_uuid();
let local_device_uuid = super::transport::resolve_qconnect_device_uuid();
let mut session = QconnectSessionState {
renderers: vec![
QconnectRendererInfo {
@@ -476,7 +476,7 @@ fn effective_renderer_snapshot_prefers_session_cursor_over_stale_app_snapshot()
..Default::default()
};
let snapshot = super::build_effective_renderer_snapshot(
let snapshot = super::session::build_effective_renderer_snapshot(
&queue,
&base_renderer,
Some(&renderer_state),
@@ -542,7 +542,7 @@ fn effective_renderer_snapshot_preserves_authoritative_renderer_next_track() {
..Default::default()
};
let snapshot = super::build_effective_renderer_snapshot(&queue, &base_renderer, None, None);
let snapshot = super::session::build_effective_renderer_snapshot(&queue, &base_renderer, None, None);
assert_eq!(
snapshot
@@ -593,7 +593,7 @@ fn visible_queue_projection_respects_remote_shuffle_order() {
..Default::default()
};
let projection = super::build_visible_queue_projection(&queue, &renderer);
let projection = super::session::build_visible_queue_projection(&queue, &renderer);
assert_eq!(
projection
@@ -640,7 +640,7 @@ fn visible_queue_projection_can_infer_current_from_next_anchor() {
..Default::default()
};
let projection = super::build_visible_queue_projection(&queue, &renderer);
let projection = super::session::build_visible_queue_projection(&queue, &renderer);
assert_eq!(
projection