Skip to main content

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}