use wit_component::{ComponentEncoder, StringEncoding, embed_component_metadata};
use wit_parser::{Resolve, UnresolvedPackageGroup};
use super::error::Wasip2Error;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Wasip2World {
CliCommand,
HttpProxy,
}
impl Wasip2World {
pub fn wit_name(self) -> &'static str {
match self {
Wasip2World::CliCommand => "wasi:cli/command",
Wasip2World::HttpProxy => "wasi:http/proxy",
}
}
pub(super) fn local_name(self) -> &'static str {
match self {
Wasip2World::CliCommand => "command",
Wasip2World::HttpProxy => "http-proxy",
}
}
}
pub fn compile_to_component(
core_wasm: &[u8],
world: Wasip2World,
) -> Result<(Vec<u8>, String), Wasip2Error> {
let mut resolve = Resolve::default();
super::wasi_bundle::push_wasi_packages(&mut resolve)?;
let needs_http = core_imports_use_wasi_http(core_wasm);
let wit_source = super::wit::emit_world_wit(world, needs_http);
let unresolved = UnresolvedPackageGroup::parse("aver-generated.wit", &wit_source)
.map_err(|e| Wasip2Error::Wrap(format!("WIT parse failed for generated package: {e}")))?;
let pkg_id = resolve
.push_group(unresolved)
.map_err(|e| Wasip2Error::Wrap(format!("push aver:user package into Resolve: {e}")))?;
let world_id = resolve
.select_world(&[pkg_id], Some(world.local_name()))
.map_err(|e| {
Wasip2Error::Wrap(format!(
"select world `{}` in generated package: {e}",
world.local_name()
))
})?;
let mut core = core_wasm.to_vec();
embed_component_metadata(&mut core, &resolve, world_id, StringEncoding::UTF8)
.map_err(|e| Wasip2Error::Wrap(format!("embed component-type metadata: {e}")))?;
let component = ComponentEncoder::default()
.module(&core)
.map_err(|e| Wasip2Error::Wrap(format!("ComponentEncoder::module rejected core: {e}")))?
.validate(true)
.encode()
.map_err(|e| Wasip2Error::Wrap(format!("ComponentEncoder::encode failed: {e}")))?;
Ok((component, wit_source))
}
fn core_imports_use_wasi_http(core_wasm: &[u8]) -> bool {
use wasmparser::{Parser, Payload};
for payload in Parser::new(0).parse_all(core_wasm).flatten() {
if let Payload::ImportSection(reader) = payload {
for import in reader.into_imports().flatten() {
if import.module.starts_with("wasi:http/") {
return true;
}
}
}
}
false
}