wascc_codec/
capabilities.rs

1//! # Common types used for managing native capability providers
2
3use std::error::Error;
4
5use std::any::Any;
6
7/// All capability providers must respond to this operation, which will be requested by
8/// the host (the `system` actor)
9pub const OP_GET_CAPABILITY_DESCRIPTOR: &str = "GetCapabilityDescriptor";
10
11/// The dispatcher is used by a native capability provider to send commands to an actor module, expecting
12/// a result containing a byte array in return
13#[deprecated = "The capability provider functionality from this crate has been moved to wasmcloud-provider-core"]
14pub trait Dispatcher: Any + Send + Sync {
15    fn dispatch(
16        &self,
17        actor: &str,
18        op: &str,
19        msg: &[u8],
20    ) -> Result<Vec<u8>, Box<dyn Error + Sync + Send>>;
21}
22
23/// Metadata describing the capability provider and the operations it supports
24#[deprecated = "The capability provider functionality from this crate has been moved to wasmcloud-provider-core"]
25#[repr(C)]
26#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
27pub struct CapabilityDescriptor {
28    /// The capability ID of the provider, e.g. `wascc:messaging` or `thirdparty:someprovider`
29    pub id: String,
30    /// The human-friendly name of the provider, displayed in short messages and log entries
31    pub name: String,
32    /// A semver string representing the version of the provider module
33    pub version: String,
34    /// A monotonicaly increasing revision number
35    pub revision: u32,
36    /// A longer, documentation-friendly, description of this provider
37    pub long_description: String,
38    /// A list of all of the operations supported by this provider
39    pub supported_operations: Vec<OperationDescriptor>,
40}
41
42impl CapabilityDescriptor {
43    pub fn builder() -> CapabilityDescriptorBuilder {
44        CapabilityDescriptorBuilder::new()
45    }
46}
47
48/// A fluent syntax builder for creating a capability descriptor
49#[derive(Default)]
50pub struct CapabilityDescriptorBuilder {
51    descriptor: CapabilityDescriptor,
52}
53
54impl CapabilityDescriptorBuilder {
55    /// Creates a new capability descriptor builder
56    fn new() -> CapabilityDescriptorBuilder {
57        CapabilityDescriptorBuilder::default()
58    }
59
60    /// Sets the capability ID (e.g. `wascc:messaging`) of the provider
61    pub fn id(self, id: &str) -> Self {
62        CapabilityDescriptorBuilder {
63            descriptor: CapabilityDescriptor {
64                id: id.to_string(),
65                ..self.descriptor
66            },
67        }
68    }
69
70    /// Sets the name of the capability provider.
71    pub fn name(self, name: &str) -> Self {
72        CapabilityDescriptorBuilder {
73            descriptor: CapabilityDescriptor {
74                name: name.to_string(),
75                ..self.descriptor
76            },
77        }
78    }
79
80    /// Sets a longer, documentation-friendly description of the provider
81    pub fn long_description(self, desc: &str) -> Self {
82        CapabilityDescriptorBuilder {
83            descriptor: CapabilityDescriptor {
84                long_description: desc.to_string(),
85                ..self.descriptor
86            },
87        }
88    }
89
90    /// Sets the version string (semver by convention) of the provider
91    pub fn version(self, ver: &str) -> Self {
92        CapabilityDescriptorBuilder {
93            descriptor: CapabilityDescriptor {
94                version: ver.to_string(),
95                ..self.descriptor
96            },
97        }
98    }
99
100    /// Sets the monotonically increasing, numeric revision number of a provider. Used when comparing provider versions
101    pub fn revision(self, rev: u32) -> Self {
102        CapabilityDescriptorBuilder {
103            descriptor: CapabilityDescriptor {
104                revision: rev,
105                ..self.descriptor
106            },
107        }
108    }
109
110    /// Adds an operation descriptor to the provider descriptor.
111    pub fn with_operation(self, name: &str, direction: OperationDirection, doctext: &str) -> Self {
112        let mut newops = self.descriptor.supported_operations;
113        newops.push(OperationDescriptor::new(name, direction, doctext));
114        CapabilityDescriptorBuilder {
115            descriptor: CapabilityDescriptor {
116                supported_operations: newops,
117                ..self.descriptor
118            },
119        }
120    }
121
122    /// Produces a new capability descriptor from the builder's configuration
123    pub fn build(self) -> CapabilityDescriptor {
124        self.descriptor
125    }
126}
127
128/// A description of a single operation supported by a capability provider
129#[deprecated = "The capability provider functionality from this crate has been moved to wasmcloud-provider-core"]
130#[repr(C)]
131#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
132pub struct OperationDescriptor {
133    /// The name of the operation. This must be unique per capability ID
134    pub name: String,
135    /// Indicates the direction of the operation (can be bi-directional)
136    pub direction: OperationDirection,
137    /// Documentation-suitable text for this operation
138    pub doctext: String,
139}
140
141impl OperationDescriptor {
142    /// Creates a new operation descriptor
143    pub fn new(name: &str, direction: OperationDirection, doctext: &str) -> OperationDescriptor {
144        OperationDescriptor {
145            name: name.to_string(),
146            direction,
147            doctext: doctext.to_string(),
148        }
149    }
150}
151
152/// Represents the direction of an operation invocation
153#[deprecated = "The capability provider functionality from this crate has been moved to wasmcloud-provider-core"]
154#[repr(C)]
155#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
156#[serde(rename_all = "snake_case")]
157pub enum OperationDirection {
158    ToActor,
159    ToProvider,
160    Both,
161}
162
163/// The NullDispatcher is as its name implies--a dispatcher that does nothing. This is convenient for
164/// initializing a capability provider with a null dispatcher, and then swapping it for a real dispatcher
165/// when the host runtime provides one configured with the appropriate channels
166#[deprecated = "The capability provider functionality from this crate has been moved to wasmcloud-provider-core"]
167#[derive(Default)]
168pub struct NullDispatcher {}
169
170impl NullDispatcher {
171    pub fn new() -> NullDispatcher {
172        NullDispatcher {}
173    }
174}
175
176impl Dispatcher for NullDispatcher {
177    fn dispatch(
178        &self,
179        _actor: &str,
180        _op: &str,
181        _msg: &[u8],
182    ) -> Result<Vec<u8>, Box<dyn Error + Send + Sync>> {
183        unimplemented!()
184    }
185}
186
187/// Every native capability provider must implement this trait. Both portable and native capability providers
188/// must respond to the following operations: `OP_BIND_ACTOR`, `OP_REMOVE_ACTOR`, `OP_GET_CAPABILITY_DESCRIPTOR`
189pub trait CapabilityProvider: CloneProvider + Send + Sync {
190    /// This function will be called on the provider when the host runtime is ready and has configured a dispatcher. This function is only ever
191    /// called _once_ for a capability provider, regardless of the number of actors being managed in the host
192    fn configure_dispatch(
193        &self,
194        dispatcher: Box<dyn Dispatcher>,
195    ) -> Result<(), Box<dyn Error + Send + Sync>>;
196    /// Invoked when an actor has requested that a provider perform a given operation
197    fn handle_call(
198        &self,
199        actor: &str,
200        op: &str,
201        msg: &[u8],
202    ) -> Result<Vec<u8>, Box<dyn Error + Send + Sync>>;
203    /// This function is called to let the capability provider know that it is being removed
204    /// from the host runtime. This gives the provider an opportunity to clean up any
205    /// resources and stop any running threads
206    fn stop(&self);
207}
208
209pub trait CloneProvider {
210    fn clone_provider(&self) -> Box<dyn CapabilityProvider>;
211}
212
213impl<T> CloneProvider for T
214where
215    T: CapabilityProvider + Clone + 'static,
216{
217    fn clone_provider(&self) -> Box<dyn CapabilityProvider> {
218        Box::new(self.clone())
219    }
220}
221
222impl Clone for Box<dyn CapabilityProvider> {
223    fn clone(&self) -> Self {
224        self.clone_provider()
225    }
226}
227
228/// Wraps a constructor inside an FFI function to allow the `CapabilityProvider` trait implementation
229/// to be instantiated and used by the host runtime
230#[macro_export]
231macro_rules! capability_provider {
232    ($provider_type:ty, $constructor:path) => {
233        #[no_mangle]
234        pub extern "C" fn __capability_provider_create(
235        ) -> *mut $crate::capabilities::CapabilityProvider {
236            let constructor: fn() -> $provider_type = $constructor;
237            let object = constructor();
238            let boxed: Box<$crate::capabilities::CapabilityProvider> = Box::new(object);
239            Box::into_raw(boxed)
240        }
241    };
242}
243
244#[cfg(test)]
245mod test {
246    use super::{CapabilityDescriptor, OperationDescriptor, OperationDirection};
247    #[test]
248    fn descriptor_certify_desired_json_format() {
249        let d = CapabilityDescriptor {
250            name: "test".to_string(),
251            id: "wascc:testing".to_string(),
252            version: "0.0.1".to_string(),
253            revision: 1,
254            long_description: "this is a test".to_string(),
255            supported_operations: vec![OperationDescriptor {
256                direction: OperationDirection::ToActor,
257                doctext: "this is a test".to_string(),
258                name: "OperationDumboDrop".to_string(),
259            }],
260        };
261        let s = serde_json::to_string(&d).unwrap();
262        assert_eq!(s, "{\"id\":\"wascc:testing\",\"name\":\"test\",\"version\":\"0.0.1\",\"revision\":1,\"long_description\":\"this is a test\",\"supported_operations\":[{\"name\":\"OperationDumboDrop\",\"direction\":\"to_actor\",\"doctext\":\"this is a test\"}]}".to_string());
263    }
264}