Skip to main content

sim_lib_plugin_wasm/
native_fallback.rs

1//! Capability-checked native plugin fallback loading.
2
3#[cfg(feature = "clap-host")]
4use sim_kernel::Result;
5#[cfg(feature = "clap-host")]
6use sim_lib_plugin_clap::{ClapHostProcessor, native::ClapHostProvider};
7#[cfg(all(feature = "lv2-host", target_os = "linux"))]
8use sim_lib_plugin_core::{AudioPluginCapability as Lv2Capability, CapabilitySet as Lv2Caps};
9#[cfg(feature = "clap-host")]
10use sim_lib_plugin_core::{AudioPluginCapability, CapabilitySet, PluginFormat, PluginLoadSpec};
11#[cfg(all(feature = "lv2-host", target_os = "linux"))]
12use sim_lib_plugin_lv2::{Lv2HostProcessor, native::Lv2HostProvider};
13
14/// Loads a CLAP plugin after checking the native-plugin capability.
15///
16/// # Errors
17///
18/// Returns a capability error when `caps` does not include
19/// [`AudioPluginCapability::NativePlugin`]. Returns an error from the provider
20/// when `spec` cannot be instantiated.
21#[cfg(feature = "clap-host")]
22pub fn load_clap_plugin<P: ClapHostProvider>(
23    caps: &CapabilitySet,
24    spec: &PluginLoadSpec,
25    provider: &P,
26) -> Result<ClapHostProcessor<P::Plugin>> {
27    caps.require(AudioPluginCapability::NativePlugin)?;
28    spec.require_format(PluginFormat::Clap)?;
29    let plugin = provider.instantiate(spec)?;
30    Ok(ClapHostProcessor::new(plugin))
31}
32
33/// Loads an LV2 plugin after checking the native-plugin capability.
34///
35/// # Errors
36///
37/// Returns a capability error when `caps` does not include
38/// [`AudioPluginCapability::NativePlugin`].
39/// Returns an error from the provider when `uri` cannot be instantiated.
40#[cfg(all(feature = "lv2-host", target_os = "linux"))]
41pub fn load_lv2_plugin<P: Lv2HostProvider>(
42    caps: &Lv2Caps,
43    uri: &str,
44    provider: &P,
45) -> sim_kernel::Result<Lv2HostProcessor<P::Plugin>> {
46    caps.require(Lv2Capability::NativePlugin)?;
47    let plugin = provider.instantiate(uri)?;
48    Ok(Lv2HostProcessor::new(plugin))
49}
50
51#[cfg(all(test, feature = "clap-host"))]
52mod tests {
53    use sim_kernel::Error;
54    use sim_lib_plugin_clap::native::FixtureClapHostProvider;
55    use sim_lib_plugin_core::{
56        AudioPluginCapability, CapabilitySet, PluginFormat, PluginInstance, PluginLoadSpec,
57    };
58
59    use super::load_clap_plugin;
60
61    #[test]
62    fn clap_load_denied_without_capability() {
63        let spec = PluginLoadSpec::new(PluginFormat::Clap, "fixture://gain").unwrap();
64        let provider = FixtureClapHostProvider::gain();
65        let err = load_clap_plugin(&CapabilitySet::empty(), &spec, &provider)
66            .expect_err("load must require native capability");
67
68        assert!(matches!(
69            err,
70            Error::CapabilityDenied { capability }
71                if capability == AudioPluginCapability::NativePlugin.as_capability_name()
72        ));
73    }
74
75    #[test]
76    fn clap_load_allowed_with_capability() {
77        let spec = PluginLoadSpec::new(PluginFormat::Clap, "fixture://gain").unwrap();
78        let provider = FixtureClapHostProvider::gain();
79        let caps = CapabilitySet::with(AudioPluginCapability::NativePlugin);
80        let processor = load_clap_plugin(&caps, &spec, &provider)
81            .expect("native capability allows fixture CLAP load");
82
83        assert_eq!(
84            processor.instance().descriptor().id.format,
85            PluginFormat::Clap
86        );
87        assert_eq!(
88            processor.instance().descriptor().id.stable_id,
89            "org.sim.gain"
90        );
91    }
92
93    #[test]
94    fn clap_load_rejects_non_clap_specs() {
95        let spec = PluginLoadSpec::new(PluginFormat::Wasm, "fixture://gain").unwrap();
96        let provider = FixtureClapHostProvider::gain();
97        let caps = CapabilitySet::with(AudioPluginCapability::NativePlugin);
98
99        assert!(load_clap_plugin(&caps, &spec, &provider).is_err());
100    }
101}
102
103#[cfg(all(test, feature = "lv2-host", target_os = "linux"))]
104mod lv2_tests {
105    use sim_kernel::Error;
106    use sim_lib_plugin_core::{AudioPluginCapability, CapabilitySet, PluginFormat, PluginInstance};
107    use sim_lib_plugin_lv2::native::FixtureLv2HostProvider;
108
109    use super::load_lv2_plugin;
110
111    #[test]
112    fn lv2_load_denied_without_capability() {
113        let provider = FixtureLv2HostProvider::gain();
114        let err = load_lv2_plugin(&CapabilitySet::empty(), provider.uri(), &provider)
115            .expect_err("load must require native capability");
116
117        assert!(matches!(
118            err,
119            Error::CapabilityDenied { capability }
120                if capability == AudioPluginCapability::NativePlugin.as_capability_name()
121        ));
122    }
123
124    #[test]
125    fn lv2_load_allowed_with_capability() {
126        let provider = FixtureLv2HostProvider::gain();
127        let caps = CapabilitySet::with(AudioPluginCapability::NativePlugin);
128        let processor = load_lv2_plugin(&caps, provider.uri(), &provider)
129            .expect("native capability allows fixture LV2 load");
130
131        assert_eq!(
132            processor.instance().descriptor().id.format,
133            PluginFormat::Lv2
134        );
135        assert_eq!(
136            processor.instance().descriptor().id.stable_id,
137            "https://sim.dev/lv2/gain"
138        );
139    }
140
141    #[test]
142    fn lv2_load_rejects_unavailable_uri() {
143        let provider = FixtureLv2HostProvider::gain();
144        let caps = CapabilitySet::with(AudioPluginCapability::NativePlugin);
145
146        assert!(load_lv2_plugin(&caps, "urn:missing:lv2", &provider).is_err());
147    }
148}