sim_lib_stream_host/
provider.rs1use std::collections::BTreeMap;
4use std::sync::Arc;
5
6use sim_kernel::{CapabilityName, Cx, Error, Lib, LibSource, LoaderRegistry, Result, Symbol};
7
8use crate::{AudioRouter, AudioSite};
9
10pub const AUDIO_PROVIDER_ENTRY_V1: &str = "sim_audio_provider_v1";
15
16pub const AUDIO_PROVIDER_ABI_VERSION: u32 = 1;
18
19pub fn native_audio_provider_capability() -> CapabilityName {
21 CapabilityName::new("audio.provider.native")
22}
23
24pub trait AudioProviderRegistrar {
26 fn register_site(&mut self, site: Arc<dyn AudioSite>);
28
29 fn host_abi_version(&self) -> u32;
31}
32
33pub type AudioProviderEntry = fn(&mut dyn AudioProviderRegistrar) -> Result<()>;
35
36#[derive(Clone, Default)]
38pub struct AudioProviderEntries {
39 entries: BTreeMap<Symbol, AudioProviderEntry>,
40}
41
42impl AudioProviderEntries {
43 pub fn new() -> Self {
45 Self::default()
46 }
47
48 pub fn with_entry(mut self, provider: Symbol, entry: AudioProviderEntry) -> Self {
50 self.insert(provider, entry);
51 self
52 }
53
54 pub fn insert(&mut self, provider: Symbol, entry: AudioProviderEntry) {
56 self.entries.insert(provider, entry);
57 }
58
59 fn entry(&self, provider: &Symbol) -> Result<AudioProviderEntry> {
60 self.entries.get(provider).copied().ok_or_else(|| {
61 Error::HostError(format!(
62 "audio provider {} did not expose {}",
63 provider, AUDIO_PROVIDER_ENTRY_V1
64 ))
65 })
66 }
67}
68
69pub struct AudioProviderHost<'a> {
71 cx: &'a mut Cx,
72 loaders: &'a LoaderRegistry,
73 entries: AudioProviderEntries,
74}
75
76impl<'a> AudioProviderHost<'a> {
77 pub fn new(cx: &'a mut Cx, loaders: &'a LoaderRegistry) -> Self {
79 Self {
80 cx,
81 loaders,
82 entries: AudioProviderEntries::new(),
83 }
84 }
85
86 pub fn with_entry(mut self, provider: Symbol, entry: AudioProviderEntry) -> Self {
88 self.entries.insert(provider, entry);
89 self
90 }
91
92 pub fn load_into(&mut self, source: LibSource, router: &mut AudioRouter) -> Result<()> {
94 self.cx.require(&native_audio_provider_capability())?;
95 let lib = self.loaders.load_lib(self.cx, source)?;
96 register_provider_lib(lib.as_ref(), &self.entries, router)
97 }
98}
99
100fn register_provider_lib(
101 lib: &dyn Lib,
102 entries: &AudioProviderEntries,
103 router: &mut AudioRouter,
104) -> Result<()> {
105 let entry = entries.entry(&lib.manifest().id)?;
106 let mut registrar = RouterAudioProviderRegistrar::new(router);
107 entry(&mut registrar)
108}
109
110pub struct RouterAudioProviderRegistrar<'a> {
112 router: &'a mut AudioRouter,
113}
114
115impl<'a> RouterAudioProviderRegistrar<'a> {
116 pub fn new(router: &'a mut AudioRouter) -> Self {
118 Self { router }
119 }
120}
121
122impl AudioProviderRegistrar for RouterAudioProviderRegistrar<'_> {
123 fn register_site(&mut self, site: Arc<dyn AudioSite>) {
124 self.router.register(site);
125 }
126
127 fn host_abi_version(&self) -> u32 {
128 AUDIO_PROVIDER_ABI_VERSION
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use std::sync::Arc;
135
136 use crate::{
137 AudioDeviceCard, AudioProviderRegistrar, AudioRouter, AudioSiteKey, FakeBackend,
138 ModeledAudioSite, RouterAudioProviderRegistrar,
139 };
140
141 #[test]
142 fn provider_seam_registers_site_into_router() {
143 let key = AudioSiteKey::new("audio/native/jack-spike");
144 let card = AudioDeviceCard::modeled(key.clone(), "JACK Provider Spike");
145 let site = Arc::new(ModeledAudioSite::new(card, Arc::new(FakeBackend::new())));
146 let mut router = AudioRouter::new();
147
148 {
149 let mut registrar = RouterAudioProviderRegistrar::new(&mut router);
150 assert_eq!(registrar.host_abi_version(), 1);
151 registrar.register_site(site);
152 }
153
154 assert!(router.site(&key).is_some());
155 }
156}