wasm_link/interface.rs
1//! Interface metadata types and traits.
2//!
3//! An interface is a contract between plugins: it declares what functions an
4//! implementer must export and what a consumer may import. Interfaces are not
5//! tied to any specific plugin - they exist as abstract specifications that
6//! plugins reference via their plugs and sockets.
7
8/// Trait for accessing interface metadata from a user-defined source.
9///
10/// Implement this trait to define how interface specifications are loaded. `wasm_link`
11/// uses this trait to discover interface contracts when linking plugins together.
12///
13/// # Associated Types
14///
15/// - `Id`: Unique identifier type for interfaces (e.g., `String`, `&'static str`, `Uuid`)
16/// - `Error`: The error type returned when metadata access fails
17/// - `Function`: The type implementing [`FunctionData`] for function metadata
18/// - `FunctionIter`: Iterator over the functions this interface declares
19/// - `ResourceIter`: Iterator over the resource types this interface declares
20pub trait InterfaceData: Sized {
21
22 /// A type used as a unique identifier for an interface
23 type Id: Clone + std::hash::Hash + Eq + std::fmt::Display + std::fmt::Debug ;
24 /// Error type for metadata access failures.
25 type Error: std::error::Error ;
26 /// Function metadata type implementing [`FunctionData`].
27 type Function: FunctionData + Clone + Send + Sync + 'static ;
28 /// Iterator over functions declared by this interface.
29 type FunctionIter<'a>: IntoIterator<Item = &'a Self::Function> where Self: 'a ;
30 /// Iterator over resource type names declared by this interface.
31 type ResourceIter<'a>: IntoIterator<Item = &'a String> where Self: 'a ;
32
33 /// Returns the unique identifier for this interface.
34 ///
35 /// # Errors
36 /// Implementations may fail if the underlying data source is unavailable.
37 fn id( &self ) -> Result<&Self::Id, Self::Error> ;
38
39 /// Returns how many plugins may/must implement this interface.
40 ///
41 /// # Errors
42 /// Implementations may fail if the underlying data source is unavailable.
43 fn cardinality( &self ) -> Result<&InterfaceCardinality, Self::Error> ;
44
45 /// Returns the WIT package name for this interface.
46 ///
47 /// # Errors
48 /// Implementations may fail if the underlying data source is unavailable.
49 fn package_name( &self ) -> Result<&str, Self::Error> ;
50
51 /// Returns the functions exported by this interface.
52 ///
53 /// # Errors
54 /// Implementations may fail if the underlying data source is unavailable.
55 fn functions( &self ) -> Result<Self::FunctionIter<'_>, Self::Error> ;
56
57 /// Returns the resource types defined by this interface.
58 ///
59 /// # Errors
60 /// Implementations may fail if the underlying data source is unavailable.
61 fn resources( &self ) -> Result<Self::ResourceIter<'_>, Self::Error> ;
62
63}
64
65/// Metadata about a function declared by an interface.
66///
67/// Provides information needed during linking to wire up cross-plugin dispatch.
68/// Each function in an interface needs metadata so `wasm_link` knows how to
69/// handle calls across plugin boundaries.
70pub trait FunctionData {
71 /// Returns the function's name as defined per the WebAssembly component model
72 /// specifications and WIT standards (e.g. `get-value`, `[constructor]counter`,
73 /// `[method]counter.increment`).
74 fn name( &self ) -> &str ;
75 /// Returns the function's return kind. `wasm_link` may do some mapping on the
76 /// returned value. This can be used to tell it to skip certain steps as it is
77 /// known ahead of the time that they are not needed.
78 fn return_kind( &self ) -> ReturnKind ;
79 /// Returns `true` if this is a method (has a `self` parameter).
80 /// This changes the behaviour of calling into a non-ExactlyOne cardinality
81 /// to only call into the plugin that created the resource given to this method
82 fn is_method( &self ) -> bool ;
83}
84
85/// Categorizes a function's return for dispatch handling.
86///
87/// Determines how return values are processed during cross-plugin dispatch.
88/// Resources require special wrapping to track ownership across plugin
89/// boundaries, while plain data can be passed through directly.
90///
91/// # Choosing the Right Variant
92///
93/// **When uncertain, use [`MayContainResources`](Self::MayContainResources).** Using
94/// `AssumeNoResources` when resources are actually present will cause resource handles
95/// to be passed through unwrapped. This can lead to undefined behavior in plugins:
96/// invalid handles, use-after-free, or calls dispatched to the wrong plugin.
97///
98/// `AssumeNoResources` is a performance optimization that skips the wrapping step.
99/// Only use it when you are certain the return type contains no resource handles
100/// anywhere in its structure (including nested within records, variants, lists, etc.).
101#[derive( Copy, Clone, Eq, PartialEq, Hash, Debug, Default )]
102pub enum ReturnKind {
103 /// Function returns nothing (void).
104 #[default] Void,
105 /// Function may return resource handles - always wraps safely.
106 ///
107 /// Use this variant whenever resources might be present in the return value,
108 /// or when you're unsure. The performance overhead of wrapping is preferable
109 /// to the undefined behavior caused by unwrapped resource handles.
110 MayContainResources,
111 /// Assumes no resource handles are present - skips wrapping for performance.
112 ///
113 /// **Warning:** Only use this if you are certain no resources are present.
114 /// If resources are returned but this variant is used, resource handles will
115 /// not be wrapped correctly, potentially causing undefined behavior in plugins.
116 /// When in doubt, use [`MayContainResources`](Self::MayContainResources) instead.
117 AssumeNoResources,
118}
119
120impl std::fmt::Display for ReturnKind {
121 fn fmt( &self, f: &mut std::fmt::Formatter ) -> Result<(), std::fmt::Error> {
122 match self {
123 Self::Void => write!( f, "Function returns no data" ),
124 Self::MayContainResources => write!( f, "Return type may contain resources" ),
125 Self::AssumeNoResources => write!( f, "Function is assumed to not return any resources" ),
126 }
127 }
128}
129
130/// Specifies how many plugins may or must implement an interface.
131///
132/// Cardinality expresses what the consumer of an interface expects:
133///
134/// - `ExactlyOne`: The consumer expects a single implementation. Dispatch returns
135/// a single value directly.
136///
137/// - `AtMostOne`: The consumer can work with zero or one implementation. Dispatch
138/// returns an `Option`.
139///
140/// - `AtLeastOne`: The consumer requires at least one implementation but can handle
141/// multiple. Dispatch returns a collection.
142///
143/// - `Any`: The consumer doesn't care how many implementations exist (including zero).
144/// Dispatch returns a collection. Useful for optional extension points.
145///
146/// The cardinality determines the [`Socket`] variant used at runtime and affects
147/// how dispatch results are wrapped.
148///
149/// [`Socket`]: crate::Socket
150#[derive( Debug, PartialEq, Eq, Copy, Clone )]
151pub enum InterfaceCardinality {
152 /// Zero or one plugin allowed. Dispatch returns `Option<T>`.
153 AtMostOne,
154 /// Exactly one plugin required. Dispatch returns `T` directly.
155 ExactlyOne,
156 /// One or more plugins required. Dispatch returns a collection.
157 AtLeastOne,
158 /// Zero or more plugins allowed. Dispatch returns a collection.
159 Any,
160}
161impl std::fmt::Display for InterfaceCardinality {
162 fn fmt( &self, f: &mut std::fmt::Formatter ) -> std::fmt::Result { write!( f, "{:?}", self )}
163}