1use std::collections::HashMap;
2use std::sync::Arc;
3
4use anyhow::{bail, Context as _};
5use tracing::{error, instrument, trace, warn};
6
7#[derive(Clone, Debug, Hash, PartialEq, Eq)]
9pub struct DynamicResource {
10 pub instance: String,
11 pub name: String,
12}
13
14#[derive(Clone, Debug, Hash, PartialEq, Eq)]
16pub enum Resource {
17 Pollable,
18 InputStream,
19 OutputStream,
20 Dynamic(Arc<DynamicResource>),
21}
22
23#[derive(Clone, Debug, Hash, PartialEq, Eq)]
25pub enum Type {
26 Bool,
27 U8,
28 U16,
29 U32,
30 U64,
31 S8,
32 S16,
33 S32,
34 S64,
35 Float32,
36 Float64,
37 Char,
38 String,
39 List(Arc<Type>),
40 Record(Arc<[Type]>),
41 Tuple(Arc<[Type]>),
42 Variant(Arc<[Option<Type>]>),
43 Enum,
44 Option(Arc<Type>),
45 Result {
46 ok: Option<Arc<Type>>,
47 err: Option<Arc<Type>>,
48 },
49 Flags,
50 Future(Option<Arc<Type>>),
51 Stream {
52 element: Option<Arc<Type>>,
53 end: Option<Arc<Type>>,
54 },
55 Resource(Resource),
56}
57
58fn resolve_iter<'a, C: FromIterator<Type>>(
59 resolve: &wit_parser::Resolve,
60 types: impl IntoIterator<Item = &'a wit_parser::Type>,
61) -> anyhow::Result<C> {
62 types
63 .into_iter()
64 .map(|ty| Type::resolve(resolve, ty))
65 .collect()
66}
67
68fn resolve_optional(
69 resolve: &wit_parser::Resolve,
70 ty: &Option<wit_parser::Type>,
71) -> anyhow::Result<Option<Type>> {
72 ty.as_ref().map(|ty| Type::resolve(resolve, ty)).transpose()
73}
74
75impl Type {
76 #[instrument(level = "trace", skip(resolve), ret)]
77 pub fn resolve_def(
78 resolve: &wit_parser::Resolve,
79 ty: &wit_parser::TypeDef,
80 ) -> anyhow::Result<Self> {
81 use wit_parser::{
82 Case, Field, Handle, Record, Result_, Stream, Tuple, TypeDef, TypeDefKind, TypeOwner,
83 Variant,
84 };
85
86 match ty {
87 TypeDef {
88 kind: TypeDefKind::Record(Record { fields, .. }),
89 ..
90 } => resolve_iter(resolve, fields.iter().map(|Field { ty, .. }| ty)).map(Type::Record),
91 TypeDef {
92 name,
93 kind: TypeDefKind::Resource,
94 owner,
95 ..
96 } => {
97 let name = name.as_ref().context("resource is missing a name")?;
98 match owner {
99 TypeOwner::Interface(interface) => {
100 let interface = resolve
101 .interfaces
102 .get(*interface)
103 .context("resource belongs to a non-existent interface")?;
104 let interface_name = interface
105 .name
106 .as_ref()
107 .context("interface is missing a name")?;
108 let pkg = interface
109 .package
110 .context("interface is missing a package")?;
111 let instance = resolve.id_of_name(pkg, interface_name);
112 match (instance.as_str(), name.as_str()) {
113 ("wasi:io/poll@0.2.0", "pollable") => {
114 Ok(Self::Resource(Resource::Pollable))
115 }
116 ("wasi:io/streams@0.2.0", "input-stream") => {
117 Ok(Self::Resource(Resource::InputStream))
118 }
119 ("wasi:io/streams@0.2.0", "output-stream") => {
120 Ok(Self::Resource(Resource::OutputStream))
121 }
122 _ => Ok(Self::Resource(Resource::Dynamic(Arc::new(
123 DynamicResource {
124 instance,
125 name: name.to_string(),
126 },
127 )))),
128 }
129 }
130 _ => bail!("only resources owned by an interface are currently supported"),
131 }
132 }
133 TypeDef {
134 kind: TypeDefKind::Handle(Handle::Own(ty) | Handle::Borrow(ty)),
135 ..
136 } => {
137 let ty = resolve
138 .types
139 .get(*ty)
140 .context("unknown handle inner type")?;
141 Self::resolve_def(resolve, ty)
142 }
143 TypeDef {
144 kind: TypeDefKind::Flags(..),
145 ..
146 } => Ok(Self::Flags),
147 TypeDef {
148 kind: TypeDefKind::Tuple(Tuple { types }),
149 ..
150 } => resolve_iter(resolve, types).map(Type::Tuple),
151 TypeDef {
152 kind: TypeDefKind::Variant(Variant { cases }),
153 ..
154 } => cases
155 .iter()
156 .map(|Case { ty, .. }| resolve_optional(resolve, ty))
157 .collect::<anyhow::Result<_>>()
158 .map(Type::Variant),
159 TypeDef {
160 kind: TypeDefKind::Enum(..),
161 ..
162 } => Ok(Type::Enum),
163 TypeDef {
164 kind: TypeDefKind::Option(ty),
165 ..
166 } => {
167 let ty =
168 Self::resolve(resolve, ty).context("failed to resolve inner option type")?;
169 Ok(Type::Option(Arc::new(ty)))
170 }
171 TypeDef {
172 kind: TypeDefKind::Result(Result_ { ok, err }),
173 ..
174 } => {
175 let ok = resolve_optional(resolve, ok)
176 .context("failed to resolve inner result `ok` variant type")?
177 .map(Arc::new);
178 let err = resolve_optional(resolve, err)
179 .context("failed to resolve inner result `err` variant type")?
180 .map(Arc::new);
181 Ok(Type::Result { ok, err })
182 }
183 TypeDef {
184 kind: TypeDefKind::List(ty),
185 ..
186 } => Self::resolve(resolve, ty)
187 .context("failed to resolve inner list type")
188 .map(Arc::new)
189 .map(Self::List),
190 TypeDef {
191 kind: TypeDefKind::Future(ty),
192 ..
193 } => {
194 let ty = resolve_optional(resolve, ty)
195 .context("failed to resolve inner future type")?
196 .map(Arc::new);
197 Ok(Type::Future(ty))
198 }
199 TypeDef {
200 kind: TypeDefKind::Stream(Stream { element, end }),
201 ..
202 } => {
203 let element = resolve_optional(resolve, element)
204 .context("failed to resolve inner stream `element` type")?
205 .map(Arc::new);
206 let end = resolve_optional(resolve, end)
207 .context("failed to resolve inner stream `end` type")?
208 .map(Arc::new);
209 Ok(Type::Stream { element, end })
210 }
211 TypeDef {
212 kind: TypeDefKind::Type(ty),
213 ..
214 } => Self::resolve(resolve, ty).context("failed to resolve inner handle type"),
215 TypeDef {
216 kind: TypeDefKind::Unknown,
217 ..
218 } => bail!("invalid type definition"),
219 }
220 }
221
222 #[instrument(level = "trace", skip(resolve), ret)]
223 pub fn resolve(resolve: &wit_parser::Resolve, ty: &wit_parser::Type) -> anyhow::Result<Self> {
224 use wit_parser::Type;
225
226 match ty {
227 Type::Bool => Ok(Self::Bool),
228 Type::U8 => Ok(Self::U8),
229 Type::U16 => Ok(Self::U16),
230 Type::U32 => Ok(Self::U32),
231 Type::U64 => Ok(Self::U64),
232 Type::S8 => Ok(Self::S8),
233 Type::S16 => Ok(Self::S16),
234 Type::S32 => Ok(Self::S32),
235 Type::S64 => Ok(Self::S64),
236 Type::Float32 => Ok(Self::Float32),
237 Type::Float64 => Ok(Self::Float64),
238 Type::Char => Ok(Self::Char),
239 Type::String => Ok(Self::String),
240 Type::Id(ty) => {
241 let ty = resolve.types.get(*ty).context("unknown type")?;
242 Self::resolve_def(resolve, ty)
243 }
244 }
245 }
246}
247
248#[derive(Debug)]
250pub enum DynamicFunction {
251 Method {
252 receiver: Arc<DynamicResource>,
253 params: Arc<Box<[Type]>>,
254 results: Arc<Box<[Type]>>,
255 },
256 Static {
257 params: Arc<Box<[Type]>>,
258 results: Arc<Box<[Type]>>,
259 },
260}
261
262impl DynamicFunction {
263 pub fn resolve(
264 resolve: &wit_parser::Resolve,
265 wit_parser::Function {
266 params,
267 results,
268 kind,
269 ..
270 }: &wit_parser::Function,
271 ) -> anyhow::Result<Self> {
272 let mut params = params
273 .iter()
274 .map(|(_, ty)| Type::resolve(resolve, ty))
275 .collect::<anyhow::Result<Vec<_>>>()
276 .context("failed to resolve parameter types")?;
277 let results = results
278 .iter_types()
279 .map(|ty| Type::resolve(resolve, ty))
280 .collect::<anyhow::Result<Vec<_>>>()
281 .context("failed to resolve result types")?;
282 let results = Arc::new(results.into());
283 match kind {
284 wit_parser::FunctionKind::Method(_) => {
285 if params.is_empty() {
286 bail!("method takes no parameters");
287 }
288 let Type::Resource(Resource::Dynamic(receiver)) = params.remove(0) else {
289 bail!("first method parameter is not a guest resource");
290 };
291 let params = Arc::new(params.into());
292 Ok(DynamicFunction::Method {
293 receiver,
294 params,
295 results,
296 })
297 }
298 _ => Ok(DynamicFunction::Static {
299 params: Arc::new(params.into()),
300 results,
301 }),
302 }
303 }
304}
305
306pub fn function_exports<'a>(
307 resolve: &wit_parser::Resolve,
308 exports: impl IntoIterator<Item = (&'a wit_parser::WorldKey, &'a wit_parser::WorldItem)>,
309) -> HashMap<String, HashMap<String, DynamicFunction>> {
310 use wit_parser::WorldItem;
311
312 exports
313 .into_iter()
314 .filter_map(|(wk, wi)| {
315 let name = resolve.name_world_key(wk);
316 match wi {
317 WorldItem::Type(_ty) => {
318 trace!(name, "type export, skip");
319 None
320 }
321 WorldItem::Function(ty) => match DynamicFunction::resolve(resolve, ty) {
322 Ok(ty) => Some((String::new(), HashMap::from([(name, ty)]))),
323 Err(err) => {
324 warn!(?err, "failed to resolve function export, skip");
325 None
326 }
327 },
328 WorldItem::Interface(interface_id) => {
329 let Some(wit_parser::Interface { functions, .. }) =
330 resolve.interfaces.get(*interface_id)
331 else {
332 warn!("component exports a non-existent interface, skip");
333 return None;
334 };
335 let functions = functions
336 .into_iter()
337 .filter_map(|(func_name, ty)| {
338 let ty = match DynamicFunction::resolve(resolve, ty) {
339 Ok(ty) => ty,
340 Err(err) => {
341 warn!(?err, "failed to resolve function export, skip");
342 return None;
343 }
344 };
345 let func_name = if let DynamicFunction::Method { .. } = ty {
346 let Some(func_name) = func_name.strip_prefix("[method]") else {
347 error!("`[method]` prefix missing in method name, skip");
348 return None;
349 };
350 func_name
351 } else {
352 func_name
353 };
354 Some((func_name.to_string(), ty))
355 })
356 .collect();
357 Some((name, functions))
358 }
359 }
360 })
361 .collect()
362}