alef 0.109.6

Opinionated polyglot binding generator for Rust libraries
Documentation
//! Explicit component API for WebAssembly targets.

use crate::core::config::ResolvedCrateConfig;

const UNSUPPORTED_MESSAGE: &str = "downloadable native components are unsupported on WebAssembly because wasm32 cannot dynamically load host libraries";

pub(super) fn generate(config: &ResolvedCrateConfig) -> Option<String> {
    if config.components.is_empty() {
        return None;
    }

    Some(format!(
        r#"fn alef_component_unsupported() -> wasm_bindgen::JsValue {{
    wasm_bindgen::JsValue::from_str("{UNSUPPORTED_MESSAGE}")
}}

/// Downloadable native components require host dynamic-library loading and are not available in WebAssembly.
#[wasm_bindgen::prelude::wasm_bindgen(js_name = componentLoad)]
pub fn component_load(_component: String) -> Result<(), wasm_bindgen::JsValue> {{
    Err(alef_component_unsupported())
}}

/// Downloadable native components require host dynamic-library loading and are not available in WebAssembly.
#[wasm_bindgen::prelude::wasm_bindgen(js_name = componentPrefetch)]
pub fn component_prefetch(_components: Option<js_sys::Array>) -> Result<js_sys::Array, wasm_bindgen::JsValue> {{
    Err(alef_component_unsupported())
}}

/// Downloadable native components require host dynamic-library loading and are not available in WebAssembly.
#[wasm_bindgen::prelude::wasm_bindgen(js_name = componentStatus)]
pub fn component_status(_component: String) -> Result<String, wasm_bindgen::JsValue> {{
    Err(alef_component_unsupported())
}}

/// Downloadable native components require host dynamic-library loading and are not available in WebAssembly.
#[wasm_bindgen::prelude::wasm_bindgen(js_name = componentCachePath)]
pub fn component_cache_path(_component: String) -> Result<String, wasm_bindgen::JsValue> {{
    Err(alef_component_unsupported())
}}"#,
    ))
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::core::config::{ComponentConfig, ComponentProvidesConfig};

    #[test]
    fn emits_explicit_unsupported_component_surface() {
        let config = ResolvedCrateConfig {
            components: vec![ComponentConfig {
                name: "fast".into(),
                provides: vec![ComponentProvidesConfig {
                    contract: "engine".into(),
                    implementation: "demo_core::FastEngine".into(),
                }],
                features: vec!["fast".into()],
                default_features: false,
                targets: Some(vec!["x86_64-unknown-linux-gnu".into()]),
                bundled_on: Vec::new(),
            }],
            ..ResolvedCrateConfig::default()
        };
        let generated = generate(&config).expect("configured components must emit an API");

        for operation in [
            "component_load",
            "component_prefetch",
            "component_status",
            "component_cache_path",
        ] {
            assert!(generated.contains(operation), "missing {operation}:\n{generated}");
        }
        assert!(generated.contains("unsupported on WebAssembly"));
        assert!(generated.contains("js_name = componentLoad"));
    }

    #[test]
    fn omits_component_surface_when_no_components_are_configured() {
        assert!(generate(&ResolvedCrateConfig::default()).is_none());
    }
}