Skip to main content

sim_lib_stream_host/placement/
device.rs

1//! Cataloged host device placement records.
2
3use sim_kernel::Symbol;
4
5use crate::model::{
6    HostOpenPlan, stream_host_capability, stream_host_device_read_effect_kind,
7    stream_host_device_write_effect_kind,
8};
9
10use super::AudioDeviceCard;
11
12/// Placement tier for a host-visible stream device.
13#[derive(Clone, Debug, PartialEq, Eq)]
14pub enum Placement {
15    /// Deterministic in-process fixture that needs no host hardware.
16    Modeled,
17    /// Real host hardware behind the named transport.
18    Hardware {
19        /// Transport responsible for opening the hardware device.
20        transport: Symbol,
21    },
22}
23
24/// Stream-device media family surfaced by the shared device catalog.
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub enum DeviceKind {
27    /// Audio PCM device.
28    Audio,
29    /// MIDI event device.
30    Midi,
31}
32
33/// Direction supported by a cataloged stream device.
34#[derive(Clone, Copy, Debug, PartialEq, Eq)]
35pub enum DeviceDirection {
36    /// Device produces stream input.
37    Input,
38    /// Device consumes stream output.
39    Output,
40    /// Device supports input and output.
41    Duplex,
42}
43
44impl DeviceDirection {
45    /// Returns the device effects performed when this catalog direction opens.
46    pub fn effect_kinds(self) -> Vec<Symbol> {
47        match self {
48            Self::Input => vec![stream_host_device_read_effect_kind()],
49            Self::Output => vec![stream_host_device_write_effect_kind()],
50            Self::Duplex => vec![
51                stream_host_device_read_effect_kind(),
52                stream_host_device_write_effect_kind(),
53            ],
54        }
55    }
56}
57
58/// Catalog row for a host-visible stream device.
59#[derive(Clone, Debug, PartialEq, Eq)]
60pub struct DeviceRecord {
61    /// Stable catalog identifier.
62    pub id: Symbol,
63    /// Human-facing device label.
64    pub display_name: String,
65    /// Device media family.
66    pub kind: DeviceKind,
67    /// Device stream direction.
68    pub direction: DeviceDirection,
69    /// Placement tier for the device.
70    pub placement: Placement,
71}
72
73impl DeviceRecord {
74    /// Builds a modeled MIDI input record.
75    pub fn modeled_midi_input(id: &str, name: impl Into<String>) -> Self {
76        Self::modeled(id, name, DeviceKind::Midi, DeviceDirection::Input)
77    }
78
79    /// Builds a modeled MIDI output record.
80    pub fn modeled_midi_output(id: &str, name: impl Into<String>) -> Self {
81        Self::modeled(id, name, DeviceKind::Midi, DeviceDirection::Output)
82    }
83
84    /// Builds a modeled audio output record.
85    pub fn modeled_audio_output(id: &str, name: impl Into<String>) -> Self {
86        Self::modeled(id, name, DeviceKind::Audio, DeviceDirection::Output)
87    }
88
89    /// Builds a modeled audio record from an audio device card.
90    pub fn modeled_audio_from_card(card: &AudioDeviceCard) -> Self {
91        Self::audio_from_card(card, Placement::Modeled)
92    }
93
94    /// Builds an audio record from an audio device card and explicit placement.
95    pub fn audio_from_card(card: &AudioDeviceCard, placement: Placement) -> Self {
96        let direction = match (card.channels_in > 0, card.channels_out > 0) {
97            (true, true) => DeviceDirection::Duplex,
98            (true, false) => DeviceDirection::Input,
99            (false, true) | (false, false) => DeviceDirection::Output,
100        };
101        Self {
102            id: card.key.0.clone(),
103            display_name: card.display_name.clone(),
104            kind: DeviceKind::Audio,
105            direction,
106            placement,
107        }
108    }
109
110    /// Builds the host-open authority plan for this cataloged device.
111    pub fn open_plan(&self) -> HostOpenPlan {
112        HostOpenPlan::new(
113            self.plan_backend_symbol(),
114            self.id.clone(),
115            self.direction.effect_kinds(),
116            vec![stream_host_capability()],
117        )
118    }
119
120    fn modeled(
121        id: &str,
122        name: impl Into<String>,
123        kind: DeviceKind,
124        direction: DeviceDirection,
125    ) -> Self {
126        Self {
127            id: Symbol::new(id),
128            display_name: name.into(),
129            kind,
130            direction,
131            placement: Placement::Modeled,
132        }
133    }
134
135    fn plan_backend_symbol(&self) -> Symbol {
136        match &self.placement {
137            Placement::Modeled => Symbol::qualified("stream/host", "modeled-catalog"),
138            Placement::Hardware { transport } => transport.clone(),
139        }
140    }
141}