greentic-component 0.4.76

High-level component loader and store for Greentic components
Documentation
use std::borrow::Cow;
use std::fs;
use std::path::PathBuf;

use anyhow::Result;
use greentic_component::wasm;
use wasm_encoder::{
    CodeSection, CustomSection, ExportKind, ExportSection, Function, FunctionSection, Instruction,
    Module, TypeSection,
};
use wit_component::{StringEncoding, metadata};
use wit_parser::{Resolve, WorldId};

const WASI_MARKER: &str = "wasm32-wasip2";
const FIXTURE_WIT: &str = r#"
package greentic:component;

world echo {
    export describe: func();
}
"#;

fn main() -> Result<()> {
    let (bytes, world_label) = build_module(FIXTURE_WIT, &["describe"]);
    let dir = PathBuf::from("crates/greentic-component/tests/fixtures/manifests/bin");
    fs::create_dir_all(&dir)?;
    let wasm_path = dir.join("component.wasm");
    fs::write(&wasm_path, &bytes)?;
    let hash = format!("blake3:{}", hex::encode(blake3::hash(&bytes).as_bytes()));
    println!("fixture world label: {world_label}");
    println!("component_wasm hash: {hash}");
    Ok(())
}

fn build_module(world_src: &str, funcs: &[&str]) -> (Vec<u8>, String) {
    let mut resolve = Resolve::default();
    let pkg = resolve
        .push_str("fixture.wit", world_src)
        .expect("push wit");
    let world = resolve
        .select_world(&[pkg], Some("echo"))
        .expect("world lookup");
    let label = world_label(&resolve, world);
    let metadata =
        metadata::encode(&resolve, world, StringEncoding::UTF8, None).expect("metadata encode");

    let mut module = Module::new();
    let mut types = TypeSection::new();
    types.ty().function([], []);
    module.section(&types);

    let mut funcs_section = FunctionSection::new();
    for _ in funcs {
        funcs_section.function(0);
    }
    module.section(&funcs_section);

    let mut exports = ExportSection::new();
    for (idx, name) in funcs.iter().enumerate() {
        exports.export(name, ExportKind::Func, idx as u32);
    }
    module.section(&exports);

    let mut code = CodeSection::new();
    for _ in funcs {
        let mut body = Function::new([]);
        body.instruction(&Instruction::End);
        code.function(&body);
    }
    module.section(&code);

    module.section(&CustomSection {
        name: "component-type".into(),
        data: Cow::Borrowed(&metadata),
    });

    module.section(&CustomSection {
        name: "producers".into(),
        data: Cow::Borrowed(WASI_MARKER.as_bytes()),
    });

    let wasm = module.finish();
    let observed = detect_world(&wasm).unwrap_or(label);
    (wasm, observed)
}

fn world_label(resolve: &Resolve, world_id: WorldId) -> String {
    let world = &resolve.worlds[world_id];
    if let Some(pkg_id) = world.package {
        let pkg = &resolve.packages[pkg_id];
        if let Some(version) = &pkg.name.version {
            format!(
                "{}:{}/{}@{}",
                pkg.name.namespace, pkg.name.name, world.name, version
            )
        } else {
            format!("{}:{}/{}", pkg.name.namespace, pkg.name.name, world.name)
        }
    } else {
        world.name.clone()
    }
}

fn detect_world(bytes: &[u8]) -> Option<String> {
    let decoded = wasm::decode_world(bytes).ok()?;
    Some(world_label(&decoded.resolve, decoded.world))
}