1use std::collections::{BTreeMap, BTreeSet};
4use std::sync::Arc;
5
6use sim_kernel::{
7 CapabilityName, Cx, Error, Export, Lib, LibManifest, LibSource, LoaderRegistry, Result, Symbol,
8};
9
10use crate::{AudioRouter, AudioSite};
11
12pub const AUDIO_PROVIDER_ENTRY_V1: &str = "sim_audio_provider_v1";
17
18pub const AUDIO_PROVIDER_ABI_VERSION: u32 = 1;
20
21pub fn native_audio_provider_capability() -> CapabilityName {
23 CapabilityName::new("audio.provider.native")
24}
25
26pub trait AudioProviderRegistrar {
28 fn register_site(&mut self, site: Arc<dyn AudioSite>);
30
31 fn host_abi_version(&self) -> u32;
33}
34
35pub type AudioProviderEntry = fn(&mut dyn AudioProviderRegistrar) -> Result<()>;
37
38#[derive(Clone, Default)]
44pub struct AudioProviderProofEntries {
45 entries: BTreeMap<Symbol, AudioProviderEntry>,
46}
47
48impl AudioProviderProofEntries {
49 pub fn new() -> Self {
51 Self::default()
52 }
53
54 pub fn with_proof_entry(mut self, provider: Symbol, entry: AudioProviderEntry) -> Self {
56 self.insert(provider, entry);
57 self
58 }
59
60 pub fn insert(&mut self, provider: Symbol, entry: AudioProviderEntry) {
62 self.entries.insert(provider, entry);
63 }
64
65 fn entry(&self, provider: &Symbol) -> Result<AudioProviderEntry> {
66 self.entries.get(provider).copied().ok_or_else(|| {
67 Error::HostError(format!(
68 "audio provider {} has no proof entry for {}",
69 provider, AUDIO_PROVIDER_ENTRY_V1
70 ))
71 })
72 }
73}
74
75pub struct AudioProviderHost<'a> {
77 cx: &'a mut Cx,
78 loaders: &'a LoaderRegistry,
79 proof_entries: AudioProviderProofEntries,
80}
81
82impl<'a> AudioProviderHost<'a> {
83 pub fn new(cx: &'a mut Cx, loaders: &'a LoaderRegistry) -> Self {
85 Self {
86 cx,
87 loaders,
88 proof_entries: AudioProviderProofEntries::new(),
89 }
90 }
91
92 pub fn with_proof_entry(mut self, provider: Symbol, entry: AudioProviderEntry) -> Self {
94 self.proof_entries.insert(provider, entry);
95 self
96 }
97
98 pub fn with_entry(self, provider: Symbol, entry: AudioProviderEntry) -> Self {
104 self.with_proof_entry(provider, entry)
105 }
106
107 pub fn load_into(&mut self, source: LibSource, router: &mut AudioRouter) -> Result<()> {
109 self.cx.require(&native_audio_provider_capability())?;
110 let lib = self.loaders.load_lib(self.cx, source)?;
111 register_provider_lib(lib.as_ref(), &self.proof_entries, router)
112 }
113}
114
115fn register_provider_lib(
116 lib: &dyn Lib,
117 proof_entries: &AudioProviderProofEntries,
118 router: &mut AudioRouter,
119) -> Result<()> {
120 let manifest = lib.manifest();
121 let site_exports = AudioProviderSiteExports::from_manifest(&manifest)?;
122 let entry = proof_entries.entry(&manifest.id)?;
123 let mut registrar =
124 RouterAudioProviderRegistrar::for_provider(router, manifest.id, site_exports.symbols);
125 entry(&mut registrar)?;
126 registrar.finish()
127}
128
129struct AudioProviderSiteExports {
130 symbols: BTreeSet<Symbol>,
131}
132
133impl AudioProviderSiteExports {
134 fn from_manifest(manifest: &LibManifest) -> Result<Self> {
135 let mut symbols = BTreeSet::new();
136 for export in &manifest.exports {
137 if let Export::Site { symbol, .. } = export {
138 if symbol.namespace.as_deref() != Some("audio/site") {
139 return Err(Error::HostError(format!(
140 "audio provider {} declared invalid site export {}; expected audio/site",
141 manifest.id, symbol
142 )));
143 }
144 symbols.insert(symbol.clone());
145 }
146 }
147 if symbols.is_empty() {
148 return Err(Error::HostError(format!(
149 "audio provider {} declared no audio/site exports",
150 manifest.id
151 )));
152 }
153 Ok(Self { symbols })
154 }
155}
156
157pub struct RouterAudioProviderRegistrar<'a> {
159 router: &'a mut AudioRouter,
160 owner: Symbol,
161 allowed_sites: Option<BTreeSet<Symbol>>,
162 registration_error: Option<Error>,
163}
164
165impl<'a> RouterAudioProviderRegistrar<'a> {
166 pub fn new(router: &'a mut AudioRouter) -> Self {
168 Self {
169 router,
170 owner: Symbol::qualified("audio/provider", "proof"),
171 allowed_sites: None,
172 registration_error: None,
173 }
174 }
175
176 pub fn for_provider(
178 router: &'a mut AudioRouter,
179 owner: Symbol,
180 allowed_sites: BTreeSet<Symbol>,
181 ) -> Self {
182 Self {
183 router,
184 owner,
185 allowed_sites: Some(allowed_sites),
186 registration_error: None,
187 }
188 }
189
190 pub fn finish(self) -> Result<()> {
192 match self.registration_error {
193 Some(err) => Err(err),
194 None => Ok(()),
195 }
196 }
197
198 fn record_error(&mut self, error: Error) {
199 if self.registration_error.is_none() {
200 self.registration_error = Some(error);
201 }
202 }
203}
204
205impl AudioProviderRegistrar for RouterAudioProviderRegistrar<'_> {
206 fn register_site(&mut self, site: Arc<dyn AudioSite>) {
207 if self.registration_error.is_some() {
208 return;
209 }
210 let site_symbol = site.key().0.clone();
211 if let Some(allowed_sites) = &self.allowed_sites
212 && !allowed_sites.contains(&site_symbol)
213 {
214 self.record_error(Error::HostError(format!(
215 "audio provider {} registered undeclared site {}",
216 self.owner, site_symbol
217 )));
218 return;
219 }
220 if let Err(err) = self.router.register_owned(self.owner.clone(), site) {
221 self.record_error(err);
222 }
223 }
224
225 fn host_abi_version(&self) -> u32 {
226 AUDIO_PROVIDER_ABI_VERSION
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use std::sync::Arc;
233
234 use crate::{
235 AudioDeviceCard, AudioProviderRegistrar, AudioRouter, AudioSiteKey, FakeBackend,
236 ModeledAudioSite, RouterAudioProviderRegistrar,
237 };
238
239 #[test]
240 fn provider_seam_registers_site_into_router() {
241 let key = AudioSiteKey::new("audio/native/jack-spike");
242 let card = AudioDeviceCard::modeled(key.clone(), "JACK Provider Spike");
243 let site = Arc::new(ModeledAudioSite::new(card, Arc::new(FakeBackend::new())));
244 let mut router = AudioRouter::new();
245
246 {
247 let mut registrar = RouterAudioProviderRegistrar::new(&mut router);
248 assert_eq!(registrar.host_abi_version(), 1);
249 registrar.register_site(site);
250 registrar.finish().unwrap();
251 }
252
253 assert!(router.site(&key).is_some());
254 }
255}