//! Every function the plugin API imports is reachable through the SDK: a new //! WIT function without a wrapper in `src/` (and so in the mock host, which //! mirrors the wrappers) fails here instead of going unnoticed by plugin //! authors. The check is textual: free functions must be called as //! `wit::::(`, resource methods as `.(`. use std::path::Path; use wit_parser::{FunctionKind, Resolve, WorldItem}; fn sdk_source() -> String { let dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("src"); let mut out = String::new(); for entry in std::fs::read_dir(dir).unwrap() { let path = entry.unwrap().path(); if path.extension().is_some_and(|e| e == "rs") { out.push_str(&std::fs::read_to_string(path).unwrap()); } } out } #[test] fn every_imported_function_has_an_sdk_wrapper() { let mut resolve = Resolve::default(); let (pkg, _) = resolve.push_dir(Path::new(env!("CARGO_MANIFEST_DIR")).join("wit")).unwrap(); let source = sdk_source(); let world = resolve.packages[pkg].worlds["plugin"]; let mut missing = Vec::new(); let mut checked = 0; for item in resolve.worlds[world].imports.values() { let WorldItem::Interface { id, .. } = item else { continue }; let iface = &resolve.interfaces[*id]; if iface.package != Some(pkg) { continue; // WASI } let module = iface.name.as_deref().unwrap().replace('-', "_"); for f in iface.functions.values() { let name = f.item_name().replace('-', "_"); let call = match f.kind { FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => format!("wit::{module}::{name}("), FunctionKind::Constructor(_) => "::new(".to_owned(), FunctionKind::Static(_) | FunctionKind::AsyncStatic(_) => format!("::{name}("), _ => format!(".{name}("), }; checked += 1; if !source.contains(&call) { missing.push(format!("{}.{} (expected `{call}`)", iface.name.as_deref().unwrap(), f.name)); } } } assert!(checked > 30, "only {checked} functions found: is the world still called `plugin`?"); assert!(missing.is_empty(), "plugin API functions without an SDK wrapper:\n {}", missing.join("\n ")); }