Skip to main content

wasm_link/
socket.rs

1//! Runtime container for plugin instances or dispatch results.
2//!
3//! A [`Socket`] holds values (plugin instances or call results) in a shape that
4//! matches an interface's [`InterfaceCardinality`]. This allows consumers to
5//! handle results appropriately based on whether they expected one plugin or many.
6//!
7//! [`InterfaceCardinality`]: crate::InterfaceCardinality
8
9use std::collections::HashMap ;
10use std::sync::{ Mutex, MutexGuard, PoisonError };
11use wasmtime::component::Val ;
12
13use crate::plugin::PluginData ;
14use crate::interface::InterfaceData ;
15use crate::plugin_instance::PluginInstance ;
16use crate::DispatchError ;
17
18
19
20/// Container for plugin instances or dispatch results.
21///
22/// The variant corresponds directly to the interface's [`InterfaceCardinality`]:
23///
24/// | Cardinality  | Socket Variant           | Contents                                     |
25/// |--------------|--------------------------|----------------------------------------------|
26/// | `ExactlyOne` | `ExactlyOne( T )`        | Single value, guaranteed present             |
27/// | `AtMostOne`  | `AtMostOne( Option<T> )` | Optional single value                        |
28/// | `AtLeastOne` | `AtLeastOne( HashMap )`  | Map of plugin ID → value, at least one entry |
29/// | `Any`        | `Any( HashMap )`         | Map of plugin ID → value, may be empty       |
30///
31/// When used with [`PluginTreeHead::dispatch`], `T` is `Result<Val, DispatchError>`.
32///
33/// [`InterfaceCardinality`]: crate::InterfaceCardinality
34/// [`PluginTreeHead::dispatch`]: crate::PluginTreeHead::dispatch
35#[derive( Debug )]
36pub enum Socket<T, Id> {
37    /// Zero or one value. Used when cardinality is `AtMostOne`.
38    AtMostOne( Option<T> ),
39    /// Exactly one value, guaranteed present. Used when cardinality is `ExactlyOne`.
40    ExactlyOne( T ),
41    /// One or more values keyed by plugin ID. Used when cardinality is `AtLeastOne`.
42    AtLeastOne( HashMap<Id, T> ),
43    /// Zero or more values keyed by plugin ID. Used when cardinality is `Any`.
44    Any( HashMap<Id, T> ),
45}
46
47impl<T, Id: Clone + std::hash::Hash + Eq> Socket<T, Id> {
48
49    pub(crate) fn map<N>( &self, mut map: impl FnMut( &T ) -> N ) -> Socket<N, Id> {
50        match self {
51            Self::AtMostOne( Option::None ) => Socket::AtMostOne( Option::None ),
52            Self::AtMostOne( Some( t ) ) => Socket::AtMostOne( Some( map( t ))),
53            Self::ExactlyOne( t ) => Socket::ExactlyOne( map( t )),
54            Self::AtLeastOne( vec ) => Socket::AtLeastOne( vec.iter().map(|( id, item ): ( &Id, _ )| ( id.clone(), map( item ) )).collect() ),
55            Self::Any( vec ) => Socket::Any( vec.iter().map(|( id, item ): ( &Id, _ )| ( id.clone(), map( item ) )).collect() ),
56        }
57    }
58
59    pub(crate) fn map_mut<N>( self, mut map: impl FnMut(T) -> N ) -> Socket<N, Id> {
60        match self {
61            Self::AtMostOne( Option::None ) => Socket::AtMostOne( Option::None ),
62            Self::AtMostOne( Some( t )) => Socket::AtMostOne( Some( map( t ))),
63            Self::ExactlyOne( t ) => Socket::ExactlyOne( map( t )),
64            Self::AtLeastOne( vec ) => Socket::AtLeastOne( vec.into_iter().map(|( id, item )| ( id, map( item ) )).collect() ),
65            Self::Any( vec ) => Socket::Any( vec.into_iter().map(|( id, item )| ( id, map( item ))).collect() ),
66        }
67    }
68}
69
70impl<P: PluginData> Socket<Mutex<PluginInstance<P>>, P::Id> {
71
72    #[allow( clippy::type_complexity )]
73    pub(crate) fn get( &self, id: &P::Id ) -> Result<Option<&Mutex<PluginInstance<P>>>,PoisonError<MutexGuard<'_, PluginInstance<P>>>> {
74        Ok( match self {
75            Self::AtMostOne( Option::None ) => None,
76            Self::AtMostOne( Some( plugin )) | Self::ExactlyOne( plugin ) => {
77                if &plugin.lock()?.id == id { Some( plugin ) } else { None }
78            },
79            Self::AtLeastOne( plugins ) | Self::Any( plugins ) => plugins.get( id ),
80        })
81    }
82
83    pub(crate) fn dispatch_function<I: InterfaceData>(
84        &self,
85        interface_path: &str,
86        function: &str,
87        has_return: bool,
88        data: &[Val],
89    ) -> Socket<Result<Val, DispatchError<I>>, P::Id> {
90        self.map(| plugin | plugin
91            .lock().map_err(|_| DispatchError::Deadlock )
92            .and_then(| mut lock | lock.dispatch( interface_path, function, has_return, data ))
93        )
94    }
95}
96
97impl<Id> From<Socket<Val, Id>> for Val {
98    fn from( socket: Socket<Val, Id> ) -> Self {
99        match socket {
100            Socket::AtMostOne( Option::None ) => Val::Option( Option::None ),
101            Socket::AtMostOne( Some( val )) => Val::Option( Some( Box::new( val ))),
102            Socket::ExactlyOne( val ) => val,
103            Socket::AtLeastOne( items )
104            | Socket::Any( items ) => Val::List( items.into_values().collect() ),
105        }
106    }
107}