model.rs (2713B)
1 //! The routing domain model. This is the authoritative in-memory state the daemon owns; 2 //! [`RoutingState::snapshot`] projects it into the wire types in [`hydra_ipc`]. 3 //! 4 //! P0 keeps this intentionally thin — sources, channel maps, monitors, and the live 5 //! `MixSnapshot` arrive with the CoreAudio engine in P1–P3. 6 7 use hydra_ipc::{DeviceSummary, RouteSummary, StateSnapshot}; 8 9 /// A virtual audio device Hydra publishes via the forked HAL driver. 10 #[derive(Debug, Clone)] 11 pub struct VirtualDevice { 12 pub uid: String, 13 pub name: String, 14 pub channels: u32, 15 } 16 17 /// A routing definition: sources mixed onto a target virtual device's channels. 18 #[derive(Debug, Clone)] 19 pub struct Route { 20 pub id: String, 21 pub target_device_uid: String, 22 /// Whether the route's CoreAudio objects are currently live. 23 pub active: bool, 24 } 25 26 /// The daemon's complete routing state. 27 #[derive(Debug, Default)] 28 pub struct RoutingState { 29 pub devices: Vec<VirtualDevice>, 30 pub routes: Vec<Route>, 31 } 32 33 impl RoutingState { 34 /// Project into a wire-safe snapshot for clients. 35 pub fn snapshot(&self) -> StateSnapshot { 36 StateSnapshot { 37 daemon_version: crate::VERSION.to_string(), 38 devices: self 39 .devices 40 .iter() 41 .map(|d| DeviceSummary { uid: d.uid.clone(), name: d.name.clone(), channels: d.channels }) 42 .collect(), 43 routes: self 44 .routes 45 .iter() 46 .map(|r| RouteSummary { 47 id: r.id.clone(), 48 target: r.target_device_uid.clone(), 49 source_count: 0, 50 active: r.active, 51 gain: 1.0, 52 muted: false, 53 peak: 0.0, 54 recording: false, 55 }) 56 .collect(), 57 } 58 } 59 } 60 61 #[cfg(test)] 62 mod tests { 63 use super::*; 64 65 #[test] 66 fn empty_snapshot_carries_version() { 67 let snap = RoutingState::default().snapshot(); 68 assert_eq!(snap.daemon_version, crate::VERSION); 69 assert!(snap.devices.is_empty()); 70 assert!(snap.routes.is_empty()); 71 } 72 73 #[test] 74 fn snapshot_projects_devices_and_routes() { 75 let state = RoutingState { 76 devices: vec![VirtualDevice { uid: "hydra:main".into(), name: "Main".into(), channels: 16 }], 77 routes: vec![Route { id: "r1".into(), target_device_uid: "hydra:main".into(), active: true }], 78 }; 79 let snap = state.snapshot(); 80 assert_eq!(snap.devices[0].channels, 16); 81 assert_eq!(snap.routes[0].target, "hydra:main"); 82 assert!(snap.routes[0].active); 83 } 84 }