wrpc_core/
lib.rs

1use std::collections::HashMap;
2use std::sync::Arc;
3
4use anyhow::{bail, Context as _};
5use tracing::{error, instrument, trace, warn};
6
7/// Dynamic resource type
8#[derive(Clone, Debug, Hash, PartialEq, Eq)]
9pub struct DynamicResource {
10    pub instance: String,
11    pub name: String,
12}
13
14/// Resource type
15#[derive(Clone, Debug, Hash, PartialEq, Eq)]
16pub enum Resource {
17    Pollable,
18    InputStream,
19    OutputStream,
20    Dynamic(Arc<DynamicResource>),
21}
22
23/// Value type
24#[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/// Dynamic function
249#[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}