wasmcloud_control_interface/types/
rpc.rs

1//! Data types used in RPC calls (usually control-related) on a wasmCloud lattice
2
3use std::collections::BTreeMap;
4
5use serde::{Deserialize, Serialize};
6
7use crate::Result;
8
9/// A host response to a request to start a component.
10///
11/// This acknowledgement confirms that the host has enough capacity to start the component
12#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
13#[non_exhaustive]
14pub struct ComponentAuctionAck {
15    /// The original component reference used for the auction
16    #[serde(default)]
17    pub(crate) component_ref: String,
18    /// The unique component identifier that the auctioner can use for this component
19    #[serde(default)]
20    pub(crate) component_id: String,
21    /// The host ID of the "bidder" for this auction.
22    #[serde(default)]
23    pub(crate) host_id: String,
24    /// Constraints that were used in the auction
25    #[serde(default)]
26    pub(crate) constraints: BTreeMap<String, String>,
27}
28
29impl ComponentAuctionAck {
30    #[must_use]
31    pub fn from_component_host_and_constraints(
32        component_ref: &str,
33        component_id: &str,
34        host_id: &str,
35        constraints: impl Into<BTreeMap<String, String>>,
36    ) -> Self {
37        Self {
38            component_ref: component_ref.into(),
39            component_id: component_id.into(),
40            host_id: host_id.into(),
41            constraints: constraints.into(),
42        }
43    }
44
45    /// Get the component ref for the auction acknowledgement
46    #[must_use]
47    pub fn component_ref(&self) -> &str {
48        self.component_ref.as_ref()
49    }
50
51    /// Get the component ID for the auction acknowledgement
52    #[must_use]
53    pub fn component_id(&self) -> &str {
54        self.component_ref.as_ref()
55    }
56
57    /// Get the host ID for the auction acknowledgement
58    #[must_use]
59    pub fn host_id(&self) -> &str {
60        self.host_id.as_ref()
61    }
62
63    /// Get the constraints acknowledged by the auction acknowledgement
64    #[must_use]
65    pub fn constraints(&self) -> &BTreeMap<String, String> {
66        &self.constraints
67    }
68
69    pub fn builder() -> ComponentAuctionAckBuilder {
70        ComponentAuctionAckBuilder::default()
71    }
72}
73
74#[derive(Default, Clone, PartialEq, Eq)]
75pub struct ComponentAuctionAckBuilder {
76    component_ref: Option<String>,
77    component_id: Option<String>,
78    host_id: Option<String>,
79    constraints: Option<BTreeMap<String, String>>,
80}
81
82impl ComponentAuctionAckBuilder {
83    #[must_use]
84    pub fn component_ref(mut self, v: String) -> Self {
85        self.component_ref = Some(v);
86        self
87    }
88
89    #[must_use]
90    pub fn component_id(mut self, v: String) -> Self {
91        self.component_id = Some(v);
92        self
93    }
94
95    #[must_use]
96    pub fn host_id(mut self, v: String) -> Self {
97        self.host_id = Some(v);
98        self
99    }
100
101    #[must_use]
102    pub fn constraints(mut self, v: BTreeMap<String, String>) -> Self {
103        self.constraints = Some(v);
104        self
105    }
106
107    pub fn build(self) -> Result<ComponentAuctionAck> {
108        Ok(ComponentAuctionAck {
109            component_ref: self
110                .component_ref
111                .ok_or_else(|| "component_ref is required".to_string())?,
112            component_id: self
113                .component_id
114                .ok_or_else(|| "component_id is required".to_string())?,
115            host_id: self
116                .host_id
117                .ok_or_else(|| "host_id is required".to_string())?,
118            constraints: self.constraints.unwrap_or_default(),
119        })
120    }
121}
122
123/// A request to locate suitable hosts for a given component
124#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
125#[non_exhaustive]
126pub struct ComponentAuctionRequest {
127    /// The image reference, file or OCI, for this component.
128    #[serde(default)]
129    pub(crate) component_ref: String,
130    /// The unique identifier to be used for this component.
131    ///
132    /// The host will ensure that no other component with the same ID is running on the host
133    pub(crate) component_id: String,
134    /// The set of constraints that must match the labels of a suitable target host
135    pub(crate) constraints: BTreeMap<String, String>,
136}
137
138impl ComponentAuctionRequest {
139    /// Get the component ref for the auction request
140    #[must_use]
141    pub fn component_ref(&self) -> &str {
142        self.component_ref.as_ref()
143    }
144
145    /// Get the component ID for the auction request
146    #[must_use]
147    pub fn component_id(&self) -> &str {
148        self.component_ref.as_ref()
149    }
150
151    /// Get the constraints for the auction request
152    #[must_use]
153    pub fn constraints(&self) -> &BTreeMap<String, String> {
154        &self.constraints
155    }
156
157    pub fn builder() -> ComponentAuctionRequestBuilder {
158        ComponentAuctionRequestBuilder::default()
159    }
160}
161
162#[derive(Default, Clone, PartialEq, Eq)]
163pub struct ComponentAuctionRequestBuilder {
164    component_ref: Option<String>,
165    component_id: Option<String>,
166    constraints: Option<BTreeMap<String, String>>,
167}
168
169impl ComponentAuctionRequestBuilder {
170    #[must_use]
171    pub fn component_ref(mut self, v: String) -> Self {
172        self.component_ref = Some(v);
173        self
174    }
175
176    #[must_use]
177    pub fn component_id(mut self, v: String) -> Self {
178        self.component_id = Some(v);
179        self
180    }
181
182    #[must_use]
183    pub fn constraints(mut self, v: BTreeMap<String, String>) -> Self {
184        self.constraints = Some(v);
185        self
186    }
187
188    pub fn build(self) -> Result<ComponentAuctionRequest> {
189        Ok(ComponentAuctionRequest {
190            component_ref: self
191                .component_ref
192                .ok_or_else(|| "component_ref is required".to_string())?,
193            component_id: self
194                .component_id
195                .ok_or_else(|| "component_id is required".to_string())?,
196            constraints: self.constraints.unwrap_or_default(),
197        })
198    }
199}
200
201/// A host response to a request to start a provider.
202///
203/// This acknowledgement confirms the host has enough capacity to
204/// start the provider and that the provider is not already running on the host
205///
206#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
207#[non_exhaustive]
208pub struct ProviderAuctionAck {
209    /// The host ID of the "bidder" for this auction
210    #[serde(default)]
211    pub(crate) host_id: String,
212    /// The original provider reference provided for the auction
213    #[serde(default)]
214    pub(crate) provider_ref: String,
215    /// The unique component identifier that the auctioner can use for this provider
216    #[serde(default)]
217    pub(crate) provider_id: String,
218    /// The constraints provided for the auction
219    #[serde(default)]
220    pub(crate) constraints: BTreeMap<String, String>,
221}
222
223impl ProviderAuctionAck {
224    /// Get the Host ID for the provider auction acknowledgement
225    #[must_use]
226    pub fn host_id(&self) -> &str {
227        self.host_id.as_ref()
228    }
229
230    /// Get the provider ref for the provider auction acknowledgement
231    #[must_use]
232    pub fn provider_ref(&self) -> &str {
233        self.provider_ref.as_ref()
234    }
235
236    /// Get the provider ID for the provider auction acknowledgement
237    #[must_use]
238    pub fn provider_id(&self) -> &str {
239        self.provider_id.as_ref()
240    }
241
242    /// Get the constraints for the provider auction acknowledgement
243    #[must_use]
244    pub fn constraints(&self) -> &BTreeMap<String, String> {
245        &self.constraints
246    }
247
248    #[must_use]
249    pub fn builder() -> ProviderAuctionAckBuilder {
250        ProviderAuctionAckBuilder::default()
251    }
252}
253
254#[derive(Default, Clone, PartialEq, Eq)]
255pub struct ProviderAuctionAckBuilder {
256    host_id: Option<String>,
257    provider_ref: Option<String>,
258    provider_id: Option<String>,
259    constraints: Option<BTreeMap<String, String>>,
260}
261
262impl ProviderAuctionAckBuilder {
263    #[must_use]
264    pub fn provider_ref(mut self, v: String) -> Self {
265        self.provider_ref = Some(v);
266        self
267    }
268
269    #[must_use]
270    pub fn provider_id(mut self, v: String) -> Self {
271        self.provider_id = Some(v);
272        self
273    }
274
275    #[must_use]
276    pub fn host_id(mut self, v: String) -> Self {
277        self.host_id = Some(v);
278        self
279    }
280
281    #[must_use]
282    pub fn constraints(mut self, v: BTreeMap<String, String>) -> Self {
283        self.constraints = Some(v);
284        self
285    }
286
287    pub fn build(self) -> Result<ProviderAuctionAck> {
288        Ok(ProviderAuctionAck {
289            provider_ref: self
290                .provider_ref
291                .ok_or_else(|| "provider_ref is required".to_string())?,
292            provider_id: self
293                .provider_id
294                .ok_or_else(|| "provider_id is required".to_string())?,
295            host_id: self
296                .host_id
297                .ok_or_else(|| "host_id is required".to_string())?,
298            constraints: self.constraints.unwrap_or_default(),
299        })
300    }
301}
302
303/// A request to locate a suitable host for a capability provider.
304///
305/// The provider's unique identity is used to rule out hosts on which the
306/// provider is already running.
307#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
308pub struct ProviderAuctionRequest {
309    /// The image reference, file or OCI, for this provider.
310    #[serde(default)]
311    pub(crate) provider_ref: String,
312
313    /// The unique identifier to be used for this provider. The host will ensure
314    /// that no other provider with the same ID is running on the host
315    pub(crate) provider_id: String,
316
317    /// The set of constraints that must match the labels of a suitable target host
318    pub(crate) constraints: BTreeMap<String, String>,
319}
320
321impl ProviderAuctionRequest {
322    /// Get the provider ref for the auction request
323    #[must_use]
324    pub fn provider_ref(&self) -> &str {
325        self.provider_ref.as_ref()
326    }
327
328    /// Get the provider ID for the auction request
329    #[must_use]
330    pub fn provider_id(&self) -> &str {
331        self.provider_id.as_ref()
332    }
333
334    /// Get the constraints acknowledged by the auction request
335    #[must_use]
336    pub fn constraints(&self) -> &BTreeMap<String, String> {
337        &self.constraints
338    }
339
340    /// Build a new [`ProviderAuctionRequest`]
341    #[must_use]
342    pub fn builder() -> ProviderAuctionRequestBuilder {
343        ProviderAuctionRequestBuilder::default()
344    }
345}
346
347#[derive(Default, Clone, PartialEq, Eq)]
348pub struct ProviderAuctionRequestBuilder {
349    provider_ref: Option<String>,
350    provider_id: Option<String>,
351    constraints: Option<BTreeMap<String, String>>,
352}
353
354impl ProviderAuctionRequestBuilder {
355    #[must_use]
356    pub fn provider_ref(mut self, v: String) -> Self {
357        self.provider_ref = Some(v);
358        self
359    }
360
361    #[must_use]
362    pub fn provider_id(mut self, v: String) -> Self {
363        self.provider_id = Some(v);
364        self
365    }
366
367    #[must_use]
368    pub fn constraints(mut self, v: BTreeMap<String, String>) -> Self {
369        self.constraints = Some(v);
370        self
371    }
372
373    pub fn build(self) -> Result<ProviderAuctionRequest> {
374        Ok(ProviderAuctionRequest {
375            provider_ref: self
376                .provider_ref
377                .ok_or_else(|| "provider_ref is required".to_string())?,
378            provider_id: self
379                .provider_id
380                .ok_or_else(|| "provider_id is required".to_string())?,
381            constraints: self.constraints.unwrap_or_default(),
382        })
383    }
384}
385
386/// A request to remove a link definition and detach the relevant component
387/// from the given provider
388#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
389#[non_exhaustive]
390pub struct DeleteInterfaceLinkDefinitionRequest {
391    /// The source component's identifier.
392    pub(crate) source_id: String,
393
394    /// Name of the link. Not providing this is equivalent to specifying Some("default")
395    #[serde(default = "default_link_name")]
396    pub(crate) name: String,
397
398    /// WIT namespace of the link, e.g. `wasi` in `wasi:keyvalue/readwrite.get`
399    pub(crate) wit_namespace: String,
400
401    /// WIT package of the link, e.g. `keyvalue` in `wasi:keyvalue/readwrite.get`
402    pub(crate) wit_package: String,
403}
404
405impl DeleteInterfaceLinkDefinitionRequest {
406    pub fn from_source_and_link_metadata(
407        source_id: &str,
408        name: &str,
409        wit_ns: &str,
410        wit_pkg: &str,
411    ) -> Self {
412        Self {
413            source_id: source_id.into(),
414            name: name.into(),
415            wit_namespace: wit_ns.into(),
416            wit_package: wit_pkg.into(),
417        }
418    }
419    /// Get the source (component/provider) ID for delete request
420    #[must_use]
421    pub fn source_id(&self) -> &str {
422        self.source_id.as_ref()
423    }
424
425    /// Get the link name for the link deletion request(or "default")
426    #[must_use]
427    pub fn link_name(&self) -> &str {
428        self.name.as_ref()
429    }
430
431    /// Get the WIT namespace relevant to the link deletion request
432    #[must_use]
433    pub fn wit_namespace(&self) -> &str {
434        self.wit_namespace.as_ref()
435    }
436
437    /// Get the WIT package relevant to the link deletion request
438    #[must_use]
439    pub fn wit_package(&self) -> &str {
440        self.wit_package.as_ref()
441    }
442
443    #[must_use]
444    pub fn builder() -> DeleteInterfaceLinkDefinitionRequestBuilder {
445        DeleteInterfaceLinkDefinitionRequestBuilder::default()
446    }
447}
448
449#[derive(Default, Clone, PartialEq, Eq)]
450pub struct DeleteInterfaceLinkDefinitionRequestBuilder {
451    source_id: Option<String>,
452    name: Option<String>,
453    wit_namespace: Option<String>,
454    wit_package: Option<String>,
455}
456
457impl DeleteInterfaceLinkDefinitionRequestBuilder {
458    pub fn source_id(mut self, v: String) -> Self {
459        self.source_id = Some(v);
460        self
461    }
462
463    pub fn name(mut self, v: String) -> Self {
464        self.name = Some(v);
465        self
466    }
467
468    pub fn wit_namespace(mut self, v: String) -> Self {
469        self.wit_namespace = Some(v);
470        self
471    }
472
473    pub fn wit_package(mut self, v: String) -> Self {
474        self.wit_package = Some(v);
475        self
476    }
477
478    pub fn build(self) -> Result<DeleteInterfaceLinkDefinitionRequest> {
479        Ok(DeleteInterfaceLinkDefinitionRequest {
480            source_id: self
481                .source_id
482                .ok_or_else(|| "source_id is required".to_string())?,
483            name: self.name.ok_or_else(|| "name is required".to_string())?,
484            wit_namespace: self
485                .wit_namespace
486                .ok_or_else(|| "wit_namespace is required".to_string())?,
487            wit_package: self
488                .wit_package
489                .ok_or_else(|| "wit_package is required".to_string())?,
490        })
491    }
492}
493
494/// Helper function to provide a default link name
495fn default_link_name() -> String {
496    "default".to_string()
497}
498
499#[cfg(test)]
500mod tests {
501    use std::collections::BTreeMap;
502
503    use super::{
504        ComponentAuctionAck, ComponentAuctionRequest, DeleteInterfaceLinkDefinitionRequest,
505        ProviderAuctionAck, ProviderAuctionRequest,
506    };
507
508    #[test]
509    fn component_auction_ack_builder() {
510        assert_eq!(
511            ComponentAuctionAck {
512                component_ref: "component_ref".into(),
513                component_id: "component_id".into(),
514                host_id: "host_id".into(),
515                constraints: BTreeMap::from([("a".into(), "b".into())])
516            },
517            ComponentAuctionAck::builder()
518                .component_ref("component_ref".into())
519                .component_id("component_id".into())
520                .host_id("host_id".into())
521                .constraints(BTreeMap::from([("a".into(), "b".into())]))
522                .build()
523                .unwrap()
524        )
525    }
526
527    #[test]
528    fn component_auction_request_builder() {
529        assert_eq!(
530            ComponentAuctionRequest {
531                component_ref: "component_ref".into(),
532                component_id: "component_id".into(),
533                constraints: BTreeMap::from([("a".into(), "b".into())])
534            },
535            ComponentAuctionRequest::builder()
536                .component_ref("component_ref".into())
537                .component_id("component_id".into())
538                .constraints(BTreeMap::from([("a".into(), "b".into())]))
539                .build()
540                .unwrap()
541        )
542    }
543
544    #[test]
545    fn provider_auction_ack_builder() {
546        assert_eq!(
547            ProviderAuctionAck {
548                provider_ref: "provider_ref".into(),
549                provider_id: "provider_id".into(),
550                host_id: "host_id".into(),
551                constraints: BTreeMap::from([("a".into(), "b".into())])
552            },
553            ProviderAuctionAck::builder()
554                .provider_ref("provider_ref".into())
555                .provider_id("provider_id".into())
556                .host_id("host_id".into())
557                .constraints(BTreeMap::from([("a".into(), "b".into())]))
558                .build()
559                .unwrap()
560        )
561    }
562
563    #[test]
564    fn provider_auction_request_builder() {
565        assert_eq!(
566            ProviderAuctionRequest {
567                provider_ref: "provider_ref".into(),
568                provider_id: "provider_id".into(),
569                constraints: BTreeMap::from([("a".into(), "b".into())])
570            },
571            ProviderAuctionRequest::builder()
572                .provider_ref("provider_ref".into())
573                .provider_id("provider_id".into())
574                .constraints(BTreeMap::from([("a".into(), "b".into())]))
575                .build()
576                .unwrap()
577        )
578    }
579
580    #[test]
581    fn delete_interface_link_definition_request_builder() {
582        assert_eq!(
583            DeleteInterfaceLinkDefinitionRequest {
584                source_id: "source_id".into(),
585                name: "name".into(),
586                wit_namespace: "wit_namespace".into(),
587                wit_package: "wit_package".into(),
588            },
589            DeleteInterfaceLinkDefinitionRequest::builder()
590                .source_id("source_id".into())
591                .name("name".into())
592                .wit_namespace("wit_namespace".into())
593                .wit_package("wit_package".into())
594                .build()
595                .unwrap()
596        )
597    }
598}