Skip to main content

sim_lib_stream_host/
provider.rs

1//! Loadable audio provider registration seam.
2
3use 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
10/// Stable entry symbol exported by a loadable audio provider.
11///
12/// The host resolves this symbol after the kernel loader acquires the provider
13/// artifact, then calls the entry point once with an [`AudioProviderRegistrar`].
14pub const AUDIO_PROVIDER_ENTRY_V1: &str = "sim_audio_provider_v1";
15
16/// ABI version currently accepted by the host-side audio provider registrar.
17pub const AUDIO_PROVIDER_ABI_VERSION: u32 = 1;
18
19/// Capability required before a native audio provider can be loaded.
20pub fn native_audio_provider_capability() -> CapabilityName {
21    CapabilityName::new("audio.provider.native")
22}
23
24/// Host-supplied registration surface for loadable audio providers.
25pub trait AudioProviderRegistrar {
26    /// Registers or replaces a native audio site owned by the provider.
27    fn register_site(&mut self, site: Arc<dyn AudioSite>);
28
29    /// Returns the host ABI version accepted by this registrar.
30    fn host_abi_version(&self) -> u32;
31}
32
33/// Host-callable audio provider entry point.
34pub type AudioProviderEntry = fn(&mut dyn AudioProviderRegistrar) -> Result<()>;
35
36/// FFI-free table of provider entry points keyed by provider library id.
37#[derive(Clone, Default)]
38pub struct AudioProviderEntries {
39    entries: BTreeMap<Symbol, AudioProviderEntry>,
40}
41
42impl AudioProviderEntries {
43    /// Builds an empty provider entry table.
44    pub fn new() -> Self {
45        Self::default()
46    }
47
48    /// Adds a provider entry, builder-style.
49    pub fn with_entry(mut self, provider: Symbol, entry: AudioProviderEntry) -> Self {
50        self.insert(provider, entry);
51        self
52    }
53
54    /// Adds or replaces a provider entry.
55    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
69/// Host that loads audio providers through the kernel loader and registers sites.
70pub struct AudioProviderHost<'a> {
71    cx: &'a mut Cx,
72    loaders: &'a LoaderRegistry,
73    entries: AudioProviderEntries,
74}
75
76impl<'a> AudioProviderHost<'a> {
77    /// Builds a provider host over a context and loader registry.
78    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    /// Adds a provider entry, builder-style.
87    pub fn with_entry(mut self, provider: Symbol, entry: AudioProviderEntry) -> Self {
88        self.entries.insert(provider, entry);
89        self
90    }
91
92    /// Acquires a provider through the loader registry and registers its sites.
93    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
110/// Registrar that wires provider sites into an [`AudioRouter`].
111pub struct RouterAudioProviderRegistrar<'a> {
112    router: &'a mut AudioRouter,
113}
114
115impl<'a> RouterAudioProviderRegistrar<'a> {
116    /// Builds a registrar over the supplied audio router.
117    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}