wrpc_types/
lib.rs

1use std::collections::HashMap;
2use std::sync::Arc;
3
4use anyhow::{bail, ensure, Context as _};
5use tracing::{error, instrument, trace, warn};
6
7/// Dynamic resource type
8#[derive(Clone, Debug, Hash, PartialEq, Eq)]
9#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
10pub struct DynamicResource {
11    pub instance: String,
12    pub name: String,
13}
14
15/// Resource type
16#[derive(Clone, Debug, Hash, PartialEq, Eq)]
17#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
18pub enum Resource {
19    Pollable,
20    InputStream,
21    OutputStream,
22    Dynamic(Arc<DynamicResource>),
23}
24
25/// Value type
26#[derive(Clone, Debug, Hash, PartialEq, Eq)]
27#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
28pub enum Type {
29    Bool,
30    U8,
31    U16,
32    U32,
33    U64,
34    S8,
35    S16,
36    S32,
37    S64,
38    F32,
39    F64,
40    Char,
41    String,
42    List(Arc<Type>),
43    Record(Arc<[Type]>),
44    Tuple(Arc<[Type]>),
45    Variant(Arc<[Option<Type>]>),
46    Enum,
47    Option(Arc<Type>),
48    Result {
49        ok: Option<Arc<Type>>,
50        err: Option<Arc<Type>>,
51    },
52    Flags,
53    Future(Option<Arc<Type>>),
54    Stream(Option<Arc<Type>>),
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                ensure!(
204                    end.is_none(),
205                    "stream end elements are deprecated and will be removed in preview 3"
206                );
207                let element = resolve_optional(resolve, element)
208                    .context("failed to resolve inner stream `element` type")?
209                    .map(Arc::new);
210                Ok(Type::Stream(element))
211            }
212            TypeDef {
213                kind: TypeDefKind::Type(ty),
214                ..
215            } => Self::resolve(resolve, ty).context("failed to resolve inner handle type"),
216            TypeDef {
217                kind: TypeDefKind::Unknown,
218                ..
219            } => bail!("invalid type definition"),
220        }
221    }
222
223    #[instrument(level = "trace", skip(resolve), ret)]
224    pub fn resolve(resolve: &wit_parser::Resolve, ty: &wit_parser::Type) -> anyhow::Result<Self> {
225        use wit_parser::Type;
226
227        match ty {
228            Type::Bool => Ok(Self::Bool),
229            Type::U8 => Ok(Self::U8),
230            Type::U16 => Ok(Self::U16),
231            Type::U32 => Ok(Self::U32),
232            Type::U64 => Ok(Self::U64),
233            Type::S8 => Ok(Self::S8),
234            Type::S16 => Ok(Self::S16),
235            Type::S32 => Ok(Self::S32),
236            Type::S64 => Ok(Self::S64),
237            Type::F32 => Ok(Self::F32),
238            Type::F64 => Ok(Self::F64),
239            Type::Char => Ok(Self::Char),
240            Type::String => Ok(Self::String),
241            Type::Id(ty) => {
242                let ty = resolve.types.get(*ty).context("unknown type")?;
243                Self::resolve_def(resolve, ty)
244            }
245        }
246    }
247}
248
249/// Dynamic function
250#[derive(Debug)]
251#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
252pub enum DynamicFunction {
253    Method {
254        receiver: Arc<DynamicResource>,
255        params: Arc<[Type]>,
256        results: Arc<[Type]>,
257    },
258    Static {
259        params: Arc<[Type]>,
260        results: Arc<[Type]>,
261    },
262}
263
264impl DynamicFunction {
265    pub fn resolve(
266        resolve: &wit_parser::Resolve,
267        wit_parser::Function {
268            params,
269            results,
270            kind,
271            ..
272        }: &wit_parser::Function,
273    ) -> anyhow::Result<Self> {
274        let mut params = params
275            .iter()
276            .map(|(_, ty)| Type::resolve(resolve, ty))
277            .collect::<anyhow::Result<Vec<_>>>()
278            .context("failed to resolve parameter types")?;
279        let results = results
280            .iter_types()
281            .map(|ty| Type::resolve(resolve, ty))
282            .collect::<anyhow::Result<Vec<_>>>()
283            .context("failed to resolve result types")?;
284        let results = results.into();
285        match kind {
286            wit_parser::FunctionKind::Method(_) => {
287                if params.is_empty() {
288                    bail!("method takes no parameters");
289                }
290                let Type::Resource(Resource::Dynamic(receiver)) = params.remove(0) else {
291                    bail!("first method parameter is not a guest resource");
292                };
293                let params = params.into();
294                Ok(DynamicFunction::Method {
295                    receiver,
296                    params,
297                    results,
298                })
299            }
300            _ => Ok(DynamicFunction::Static {
301                params: params.into(),
302                results,
303            }),
304        }
305    }
306}
307
308pub fn function_exports<'a>(
309    resolve: &wit_parser::Resolve,
310    exports: impl IntoIterator<Item = (&'a wit_parser::WorldKey, &'a wit_parser::WorldItem)>,
311) -> HashMap<String, HashMap<String, DynamicFunction>> {
312    use wit_parser::WorldItem;
313
314    exports
315        .into_iter()
316        .filter_map(|(wk, wi)| {
317            let name = resolve.name_world_key(wk);
318            match wi {
319                WorldItem::Type(_ty) => {
320                    trace!(name, "type export, skip");
321                    None
322                }
323                WorldItem::Function(ty) => match DynamicFunction::resolve(resolve, ty) {
324                    Ok(ty) => Some((String::new(), HashMap::from([(name, ty)]))),
325                    Err(err) => {
326                        warn!(?err, "failed to resolve function export, skip");
327                        None
328                    }
329                },
330                WorldItem::Interface(interface_id) => {
331                    let Some(wit_parser::Interface { functions, .. }) =
332                        resolve.interfaces.get(*interface_id)
333                    else {
334                        warn!("component exports a non-existent interface, skip");
335                        return None;
336                    };
337                    let functions = functions
338                        .into_iter()
339                        .filter_map(|(func_name, ty)| {
340                            let ty = match DynamicFunction::resolve(resolve, ty) {
341                                Ok(ty) => ty,
342                                Err(err) => {
343                                    warn!(?err, "failed to resolve function export, skip");
344                                    return None;
345                                }
346                            };
347                            let func_name = if let DynamicFunction::Method { .. } = ty {
348                                let Some(func_name) = func_name.strip_prefix("[method]") else {
349                                    error!("`[method]` prefix missing in method name, skip");
350                                    return None;
351                                };
352                                func_name
353                            } else {
354                                func_name
355                            };
356                            Some((func_name.to_string(), ty))
357                        })
358                        .collect();
359                    Some((name, functions))
360                }
361            }
362        })
363        .collect()
364}
365
366#[cfg(test)]
367mod tests {
368    #[cfg(feature = "serde")]
369    #[test]
370    fn serde() {
371        use super::*;
372
373        let buf = serde_json::to_vec(&Type::U8).unwrap();
374        let ty: Type = serde_json::from_slice(&buf).unwrap();
375        assert_eq!(ty, Type::U8)
376    }
377}