use include_dir::{Dir, include_dir};
pub const CONTRACT_VERSION: &str = "0.3.0";
static EMBEDDED: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/embedded-wit/$CARGO_PKG_VERSION");
pub struct WitFile {
pub name: &'static str,
pub bytes: &'static [u8],
}
pub fn wit_files() -> Vec<WitFile> {
EMBEDDED
.files()
.filter_map(|f| {
f.path()
.file_name()
.and_then(|s| s.to_str())
.map(|name| WitFile {
name,
bytes: f.contents(),
})
})
.collect()
}
pub fn files_for_kind(kind: &str) -> anyhow::Result<Vec<WitFile>> {
if kind == "mcp" {
return Ok(Vec::new());
}
let kind_file = match kind {
"design" | "bundle" | "deploy" | "provider" | "addon" => format!("extension-{kind}.wit"),
"wasm-component" | "llm" => "extension-design.wit".to_string(),
other => anyhow::bail!("no embedded WIT files for scaffold kind `{other}`"),
};
Ok(wit_files()
.into_iter()
.filter(|f| {
matches!(f.name, "extension-base.wit" | "extension-host.wit") || f.name == kind_file
})
.collect())
}
#[must_use]
pub fn package_version(bytes: &[u8]) -> Option<String> {
let text = std::str::from_utf8(bytes).ok()?;
let decl_line = text
.lines()
.find(|l| l.trim_start().starts_with("package "))?;
let at = decl_line.find('@')?;
let after = &decl_line[at + 1..];
let semi = after.find(';').unwrap_or(after.len());
Some(after[..semi].trim().to_string())
}
pub fn package_version_for(suffix: &str) -> Option<String> {
let file = format!("extension-{suffix}.wit");
wit_files()
.into_iter()
.find(|f| f.name == file)
.and_then(|f| package_version(f.bytes))
}
pub fn sha256_hex(bytes: &[u8]) -> String {
use sha2::{Digest, Sha256};
let digest = Sha256::digest(bytes);
let mut out = String::with_capacity(64);
for b in digest {
out.push(char::from_digit(u32::from(b >> 4), 16).unwrap_or('0'));
out.push(char::from_digit(u32::from(b & 0xf), 16).unwrap_or('0'));
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn wit_files_returns_all_embedded_packages() {
let files = wit_files();
assert!(files.iter().any(|f| f.name == "extension-base.wit"));
assert!(files.iter().any(|f| f.name == "extension-host.wit"));
assert!(files.iter().any(|f| f.name == "extension-design.wit"));
assert!(files.iter().any(|f| f.name == "extension-bundle.wit"));
assert!(files.iter().any(|f| f.name == "extension-deploy.wit"));
assert!(files.iter().any(|f| f.name == "extension-provider.wit"));
assert!(files.iter().any(|f| f.name == "extension-dw-composer.wit"));
assert!(files.iter().any(|f| f.name == "runtime-side.wit"));
assert!(files.iter().any(|f| f.name == "extension-addon.wit"));
assert_eq!(files.len(), 9);
}
#[test]
fn files_for_kind_design_includes_base_host_and_design() {
let files = files_for_kind("design").expect("design resolves");
let names: Vec<_> = files.iter().map(|f| f.name).collect();
assert!(names.contains(&"extension-base.wit"));
assert!(names.contains(&"extension-host.wit"));
assert!(names.contains(&"extension-design.wit"));
assert!(!names.contains(&"extension-bundle.wit"));
}
#[test]
fn files_for_kind_bundle_includes_bundle_not_design() {
let files = files_for_kind("bundle").expect("bundle resolves");
let names: Vec<_> = files.iter().map(|f| f.name).collect();
assert!(names.contains(&"extension-bundle.wit"));
assert!(!names.contains(&"extension-design.wit"));
}
#[test]
fn files_for_kind_provider_includes_provider_not_design() {
let files = files_for_kind("provider").expect("provider resolves");
let names: Vec<_> = files.iter().map(|f| f.name).collect();
assert!(names.contains(&"extension-base.wit"));
assert!(names.contains(&"extension-host.wit"));
assert!(names.contains(&"extension-provider.wit"));
assert!(!names.contains(&"extension-design.wit"));
assert!(!names.contains(&"extension-bundle.wit"));
assert!(!names.contains(&"extension-deploy.wit"));
}
#[test]
fn files_for_kind_llm_uses_design_wit() {
let files = files_for_kind("llm").expect("llm resolves");
let names: Vec<_> = files.iter().map(|f| f.name).collect();
assert!(names.contains(&"extension-base.wit"));
assert!(names.contains(&"extension-host.wit"));
assert!(names.contains(&"extension-design.wit"));
assert!(!names.contains(&"extension-bundle.wit"));
assert!(!names.contains(&"extension-deploy.wit"));
assert!(!names.contains(&"extension-provider.wit"));
}
#[test]
fn files_for_kind_mcp_pulls_no_greentic_wit() {
let files = files_for_kind("mcp").expect("mcp resolves");
assert!(
files.is_empty(),
"mcp (wasix:mcp/router) must not bundle greentic WIT deps, got: {:?}",
files.iter().map(|f| f.name).collect::<Vec<_>>()
);
}
#[test]
fn files_for_kind_rejects_an_unknown_kind() {
let err = match files_for_kind("widget") {
Ok(files) => panic!(
"unknown kind must error, got {:?}",
files.iter().map(|f| f.name).collect::<Vec<_>>()
),
Err(e) => e,
};
assert!(
err.to_string().contains("widget"),
"error must name the offending kind: {err}"
);
}
#[test]
fn files_for_kind_addon_includes_base_host_and_addon() {
let files = files_for_kind("addon").expect("addon resolves");
let names: Vec<_> = files.iter().map(|f| f.name).collect();
assert!(names.contains(&"extension-base.wit"));
assert!(names.contains(&"extension-host.wit"));
assert!(names.contains(&"extension-addon.wit"));
assert!(!names.contains(&"extension-design.wit"));
assert!(!names.contains(&"extension-provider.wit"));
assert_eq!(names.len(), 3);
}
#[test]
fn every_scaffoldable_kind_resolves_wit_files_or_is_the_mcp_exemption() {
use crate::scaffold::Kind;
use clap::ValueEnum as _;
for kind in Kind::value_variants() {
let kind_str = kind.as_str();
let files = files_for_kind(kind_str)
.unwrap_or_else(|e| panic!("{kind_str} must resolve to WIT files: {e}"));
let names: Vec<_> = files.iter().map(|f| f.name).collect();
if kind_str == "mcp" {
assert!(
names.is_empty(),
"mcp is documented to pull no greentic WIT files, got: {names:?}"
);
continue;
}
assert!(
names.len() > 2,
"{kind_str} must yield more than just base+host, got: {names:?}"
);
}
}
#[test]
fn package_version_reads_the_declared_at_version() {
assert_eq!(
package_version(b"package greentic:extension-host@0.1.0;\n"),
Some("0.1.0".to_string())
);
assert_eq!(
package_version(b"package greentic:extension-design@0.3.0;\n\ninterface tools {}"),
Some("0.3.0".to_string())
);
}
#[test]
fn package_version_matches_the_pinned_embedded_files() {
let expected: &[(&str, &str)] = &[
("extension-base.wit", "0.3.0"),
("extension-host.wit", "0.1.0"),
("extension-design.wit", "0.3.0"),
];
for (name, want) in expected {
let file = wit_files()
.into_iter()
.find(|f| f.name == *name)
.unwrap_or_else(|| panic!("embedded {name} missing"));
assert_eq!(package_version(file.bytes).as_deref(), Some(*want));
}
}
#[test]
fn sha256_hex_is_deterministic() {
let a = sha256_hex(b"hello");
let b = sha256_hex(b"hello");
assert_eq!(a, b);
assert_eq!(a.len(), 64);
}
}