1#[cfg(not(target_arch = "wasm32"))]
7mod adapter;
8mod canonical;
9mod direct;
10mod memory;
11#[cfg(not(target_arch = "wasm32"))]
12mod package;
13mod parser;
14#[cfg(not(target_arch = "wasm32"))]
15mod runtime;
16mod syntax;
17pub mod wit;
18mod wit_format;
19mod wit_parser;
20
21#[cfg(test)]
22mod tests;
23
24use std::collections::{BTreeMap, BTreeSet};
25
26use sha2::{Digest, Sha256};
27
28use crate::extension::ExtensionExport;
29
30pub use crate::direct_wasm::{
31 DirectWasmFunctionExport, DirectWasmImport, DirectWasmImportKind, DirectWasmInspection,
32 DirectWasmMemory,
33};
34#[cfg(not(target_arch = "wasm32"))]
35pub use adapter::{
36 generate_adapter, generate_hta_adapter, verify_hta_scalar, AdapterArtifact,
37 ADAPTER_MANIFEST_SCHEMA,
38};
39pub use direct::{
40 direct_inspection_source, direct_interface_skeleton, inspect_direct,
41 DIRECT_WASM_INSPECTION_SCHEMA,
42};
43pub use memory::{
44 MemoryArgumentPlan, MemoryBindingPlan, MemoryFunctionPlan, MemoryResultPlan,
45 MEMORY_BINDING_SCHEMA,
46};
47#[cfg(not(target_arch = "wasm32"))]
48pub use package::{
49 bind_package, inspect_module, write_interface_skeleton, BindingTarget, BoundPackage,
50 InspectionArtifact, DIRECT_WASM_BINDING_SCHEMA, DIRECT_WASM_BUILD_PRODUCT_SCHEMA,
51 DIRECT_WASM_CONFORMANCE_SCHEMA,
52};
53#[cfg(not(target_arch = "wasm32"))]
54pub use runtime::WasmtimeMemoryExecutor;
55pub use wit::{
56 import_wit, project_wit, WitDiagnostic, WitDiagnosticSeverity, WitImportArtifact,
57 WitImportOptions, WitProjectionArtifact, WitProjectionOptions, WitRoute, WIT_IR_SCHEMA,
58 WIT_MANIFEST_SCHEMA,
59};
60
61pub const WASM_INTERFACE_SCHEMA: &str = "hara.wasm-interface/0-alpha";
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
64pub enum WasmValueType {
65 I32,
66 I64,
67 F32,
68 F64,
69 Void,
70}
71
72impl WasmValueType {
73 pub fn as_keyword(self) -> &'static str {
74 match self {
75 Self::I32 => "i32",
76 Self::I64 => "i64",
77 Self::F32 => "f32",
78 Self::F64 => "f64",
79 Self::Void => "void",
80 }
81 }
82}
83
84#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
85pub enum HaraValueType {
86 I32,
87 I64,
88 F32,
89 F64,
90 Boolean,
91 String,
92 Bytes,
93 Record(String),
94 Variant(String),
95 Handle(String),
96 Callback(String),
97 Void,
98}
99
100impl HaraValueType {
101 fn direct_wasm_type(&self) -> Option<WasmValueType> {
102 match self {
103 Self::I32 | Self::Boolean => Some(WasmValueType::I32),
104 Self::I64 => Some(WasmValueType::I64),
105 Self::F32 => Some(WasmValueType::F32),
106 Self::F64 => Some(WasmValueType::F64),
107 Self::Void => Some(WasmValueType::Void),
108 _ => None,
109 }
110 }
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq)]
114pub enum Ownership {
115 Borrowed,
116 Caller,
117 Callee,
118 Transferred,
119}
120
121#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub enum Lowering {
123 Direct,
124 PointerLength,
125}
126
127#[derive(Debug, Clone, Copy, PartialEq, Eq)]
128pub enum Lifting {
129 Direct,
130 PointerLength,
131 PackedI64,
132}
133
134#[derive(Debug, Clone, PartialEq, Eq)]
135pub struct MemoryContract {
136 pub export: String,
137 pub allocate: Option<String>,
138 pub reallocate: Option<String>,
139 pub release: Option<String>,
140}
141
142#[derive(Debug, Clone, PartialEq, Eq)]
143pub struct BindingParameter {
144 pub name: String,
145 pub hara_type: HaraValueType,
146 pub wasm_type: WasmValueType,
147 pub lowering: Option<Lowering>,
148 pub ownership: Option<Ownership>,
149}
150
151#[derive(Debug, Clone, PartialEq, Eq)]
152pub struct BindingResult {
153 pub hara_type: HaraValueType,
154 pub wasm_type: WasmValueType,
155 pub lifting: Option<Lifting>,
156 pub ownership: Option<Ownership>,
157}
158
159#[derive(Debug, Clone, PartialEq, Eq)]
160pub struct ErrorContract {
161 pub convention: String,
162 pub codes: BTreeMap<i64, String>,
163}
164
165#[derive(Debug, Clone, Default, PartialEq, Eq)]
166pub struct RequestPolicy {
167 pub timeout_ms: Option<u64>,
168 pub max_in_flight: Option<u32>,
169}
170
171#[derive(Debug, Clone, Copy, PartialEq, Eq)]
172pub enum CancellationPolicy {
173 Cooperative,
174 Abort,
175 Ignore,
176}
177
178#[derive(Debug, Clone, PartialEq, Eq)]
179pub struct AsyncPolicy {
180 pub operation: String,
181 pub request: RequestPolicy,
182 pub cancellation: CancellationPolicy,
183}
184
185#[derive(Debug, Clone, PartialEq, Eq)]
186pub struct HostCallContract {
187 pub methods: BTreeSet<String>,
188 pub capabilities: BTreeSet<String>,
189}
190
191#[derive(Debug, Clone, PartialEq, Eq)]
192pub struct CallbackParameter {
193 pub name: String,
194 pub hara_type: HaraValueType,
195}
196
197#[derive(Debug, Clone, PartialEq, Eq)]
198pub struct CallbackContract {
199 pub arguments: Vec<CallbackParameter>,
200 pub returns: HaraValueType,
201 pub reentrant: bool,
202}
203
204#[derive(Debug, Clone, PartialEq, Eq)]
205pub struct HandleContract {
206 pub tag: String,
207 pub release: Option<String>,
208}
209
210#[derive(Debug, Clone, PartialEq, Eq)]
211pub struct BindingFunction {
212 pub name: String,
213 pub wasm_export: String,
214 pub arguments: Vec<BindingParameter>,
215 pub returns: BindingResult,
216 pub asynchronous: bool,
217 pub operation: Option<String>,
218 pub request: Option<RequestPolicy>,
219 pub cancellation: Option<CancellationPolicy>,
220 pub errors: Option<ErrorContract>,
221 pub capabilities: BTreeSet<String>,
222}
223
224#[derive(Debug, Clone, PartialEq, Eq)]
225pub struct WasmInterface {
226 pub schema: String,
227 pub namespace: String,
228 pub module: String,
229 pub memory: Option<MemoryContract>,
230 pub exports: Vec<BindingFunction>,
231 pub capabilities: BTreeSet<String>,
232 pub host_calls: BTreeMap<String, HostCallContract>,
233 pub callbacks: BTreeMap<String, CallbackContract>,
234 pub handles: BTreeMap<String, HandleContract>,
235 pub resources: BTreeMap<String, HandleContract>,
236}
237
238impl WasmInterface {
239 pub fn parse(source: &str, origin: &str) -> Result<Self, String> {
240 parser::parse_interface(source, origin)
241 }
242
243 pub fn canonical_source(&self) -> String {
244 canonical::source(self)
245 }
246
247 pub fn digest(&self) -> String {
248 let digest = Sha256::digest(self.canonical_source().as_bytes());
249 format!("sha256:{digest:x}")
250 }
251
252 pub fn hta_required(&self) -> bool {
253 !self.host_calls.is_empty()
254 || !self.callbacks.is_empty()
255 || !self.handles.is_empty()
256 || !self.resources.is_empty()
257 || self.exports.iter().any(|export| {
258 export.asynchronous
259 || export.operation.is_some()
260 || export.request.is_some()
261 || export.cancellation.is_some()
262 })
263 }
264
265 pub fn direct_exports(&self) -> Vec<(String, ExtensionExport)> {
266 self.exports
267 .iter()
268 .map(|export| {
269 (
270 export.wasm_export.clone(),
271 ExtensionExport {
272 arguments: export
273 .arguments
274 .iter()
275 .map(|argument| argument.wasm_type.as_keyword().to_owned())
276 .collect(),
277 returns: export.returns.wasm_type.as_keyword().to_owned(),
278 asynchronous: false,
279 raw_export: None,
280 },
281 )
282 })
283 .collect()
284 }
285}