1#![deny(missing_docs)]
4#![cfg_attr(docsrs, feature(doc_cfg))]
5
6use std::str::FromStr;
7use std::{borrow::Cow, fmt::Display};
8
9use anyhow::{Result, bail};
10use wasm_encoder::{CanonicalOption, Encode, Section};
11use wit_parser::{Resolve, WorldId};
12
13mod encoding;
14mod gc;
15mod linking;
16mod printing;
17mod targets;
18mod validation;
19
20pub use encoding::{ComponentEncoder, LibraryInfo, encode};
21pub use linking::Linker;
22pub use printing::*;
23pub use targets::*;
24pub use validation::AdapterModuleDidNotExport;
25pub use wit_parser::decoding::{DecodedWasm, decode, decode_reader};
26
27pub mod metadata;
28
29#[cfg(feature = "dummy-module")]
30pub use dummy::dummy_module;
31#[cfg(feature = "dummy-module")]
32mod dummy;
33
34#[cfg(feature = "semver-check")]
35mod semver_check;
36#[cfg(feature = "semver-check")]
37pub use semver_check::*;
38
39#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
41pub enum StringEncoding {
42 #[default]
44 UTF8,
45 UTF16,
47 CompactUTF16,
49}
50
51impl Display for StringEncoding {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 match self {
54 StringEncoding::UTF8 => write!(f, "utf8"),
55 StringEncoding::UTF16 => write!(f, "utf16"),
56 StringEncoding::CompactUTF16 => write!(f, "compact-utf16"),
57 }
58 }
59}
60
61impl FromStr for StringEncoding {
62 type Err = anyhow::Error;
63
64 fn from_str(s: &str) -> Result<Self> {
65 match s {
66 "utf8" => Ok(StringEncoding::UTF8),
67 "utf16" => Ok(StringEncoding::UTF16),
68 "compact-utf16" => Ok(StringEncoding::CompactUTF16),
69 _ => bail!("unknown string encoding `{}`", s),
70 }
71 }
72}
73
74impl From<StringEncoding> for wasm_encoder::CanonicalOption {
75 fn from(e: StringEncoding) -> wasm_encoder::CanonicalOption {
76 match e {
77 StringEncoding::UTF8 => CanonicalOption::UTF8,
78 StringEncoding::UTF16 => CanonicalOption::UTF16,
79 StringEncoding::CompactUTF16 => CanonicalOption::CompactUTF16,
80 }
81 }
82}
83
84pub(crate) fn base_producers() -> wasm_metadata::Producers {
87 let mut producer = wasm_metadata::Producers::empty();
88 producer.add("processed-by", "wit-component", env!("CARGO_PKG_VERSION"));
89 producer
90}
91
92pub fn embed_component_metadata(
94 bytes: &mut Vec<u8>,
95 wit_resolver: &Resolve,
96 world: WorldId,
97 encoding: StringEncoding,
98) -> Result<()> {
99 let encoded = metadata::encode(&wit_resolver, world, encoding, None)?;
100
101 let section = wasm_encoder::CustomSection {
102 name: "component-type".into(),
103 data: Cow::Borrowed(&encoded),
104 };
105 bytes.push(section.id());
106 section.encode(bytes);
107
108 Ok(())
109}
110
111#[cfg(test)]
112mod tests {
113 use anyhow::Result;
114 use wasmparser::Payload;
115 use wit_parser::Resolve;
116
117 use super::{StringEncoding, embed_component_metadata};
118
119 const MODULE_WAT: &str = r#"
120(module
121 (type (;0;) (func))
122 (func (;0;) (type 0)
123 nop
124 )
125)
126"#;
127
128 const COMPONENT_WIT: &str = r#"
129package test:foo;
130world test-world {}
131"#;
132
133 #[test]
134 fn component_metadata_embedding_works() -> Result<()> {
135 let mut bytes = wat::parse_str(MODULE_WAT)?;
136
137 let original_len = bytes.len();
139 let payloads = wasmparser::Parser::new(0).parse_all(&bytes);
140 let original_custom_section_count = payloads.fold(0, |acc, payload| {
141 if let Ok(Payload::CustomSection { .. }) = payload {
142 acc + 1
143 } else {
144 acc
145 }
146 });
147
148 let mut resolver = Resolve::default();
150 let pkg = resolver.push_str("in-code.wit", COMPONENT_WIT)?;
151 let world = resolver.select_world(&[pkg], Some("test-world"))?;
152
153 embed_component_metadata(&mut bytes, &resolver, world, StringEncoding::UTF8)?;
155
156 let mut found_component_section = false;
158 let new_custom_section_count =
159 wasmparser::Parser::new(0)
160 .parse_all(&bytes)
161 .fold(0, |acc, payload| {
162 if let Ok(Payload::CustomSection(reader)) = payload {
163 if reader.name() == "component-type" {
164 found_component_section = true;
165 }
166 acc + 1
167 } else {
168 acc
169 }
170 });
171
172 assert!(original_len < bytes.len());
173 assert_eq!(original_custom_section_count + 1, new_custom_section_count);
174 assert!(found_component_section);
175
176 Ok(())
177 }
178}