wasmcloud_control_interface/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! # Control Interface Client
//!
//! This library provides a client API for consuming the [wasmCloud control interface][docs-control-interface]
//! over a NATS connection.
//!
//! This library can be used by multiple types of tools, and is also used
//! by the control interface capability provider and the [`wash` CLI][wash-cli].
//!
//! ## Usage
//!
//! All of the [`Client`] functions are handled by a wasmCloud host running in the specified lattice.
//!
//! Each function returns a `Result<CtlResponse<T>>` wrapper around the actual response type. The outer
//! result should be handled for protocol (timeouts, no hosts available) and deserialization errors (invalid response payload).
//! The inner result is the actual response from the host(s) and should be handled for application-level errors.
//!
//! [docs-control-interface]: <https://wasmcloud.com/docs/hosts/lattice-protocols/control-interface>
//! [wash-cli]: <https://wasmcloud.com/docs/ecosystem/wash>

use serde::{Deserialize, Serialize};

mod broker;
mod otel;

pub mod client;
pub use client::{Client, ClientBuilder};

mod types;
pub use types::component::*;
pub use types::ctl::*;
pub use types::host::*;
pub use types::link::*;
pub use types::provider::*;
pub use types::registry::*;
pub use types::rpc::*;

// NOTE(brooksmtownsend): These are included to avoid a major breaking change
// in this crate by removing the public type aliases. They should be removed
// when we release 3.0.0 of this crate.
#[deprecated(
    since = "2.3.0",
    note = "String type aliases are deprecated, use Strings instead"
)]
#[allow(dead_code)]
mod aliases {
    type ComponentId = String;
    type KnownConfigName = String;
    type LatticeTarget = String;
    type LinkName = String;
    type WitInterface = String;
    type WitNamespace = String;
    type WitPackage = String;
}
#[allow(unused_imports)]
#[allow(deprecated)]
pub use aliases::*;

/// Generic result
type Result<T> = ::core::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;

/// Enumeration of all kinds of identifiers in a wasmCloud lattice
#[allow(unused)]
pub(crate) enum IdentifierKind {
    /// Identifiers that are host IDs
    HostId,
    /// Identifiers that are component IDs
    ComponentId,
    /// Identifiers that are component references
    ComponentRefs,
    /// Identifiers that are provider reverences
    ProviderRef,
    /// Identifiers that are link names
    LinkName,
}

impl IdentifierKind {
    /// Ensure an identifier is a valid as a host ID
    fn is_host_id(value: impl AsRef<str>) -> Result<String> {
        assert_non_empty_string(value, "Host ID cannot be empty")
    }

    /// Ensure an identifier is a valid as a component ID
    fn is_component_id(value: impl AsRef<str>) -> Result<String> {
        assert_non_empty_string(value, "Component ID cannot be empty")
    }

    /// Ensure an identifier is a valid as a component reference
    fn is_component_ref(value: impl AsRef<str>) -> Result<String> {
        assert_non_empty_string(value, "Component OCI reference cannot be empty")
    }

    /// Ensure an identifier is a valid as a provider reference
    fn is_provider_ref(value: impl AsRef<str>) -> Result<String> {
        assert_non_empty_string(value, "Provider OCI reference cannot be empty")
    }

    /// Ensure an identifier is a valid as a provider reference
    fn is_provider_id(value: impl AsRef<str>) -> Result<String> {
        assert_non_empty_string(value, "Provider ID cannot be empty")
    }

    /// Ensure an identifier is a valid as a link name
    fn is_link_name(value: impl AsRef<str>) -> Result<String> {
        assert_non_empty_string(value, "Link Name cannot be empty")
    }
}

/// Helper function that serializes the data and maps the error
pub(crate) fn json_serialize<T>(item: T) -> Result<Vec<u8>>
where
    T: Serialize,
{
    serde_json::to_vec(&item).map_err(|e| format!("JSON serialization failure: {e}").into())
}

/// Helper function that deserializes the data and maps the error
pub(crate) fn json_deserialize<'de, T: Deserialize<'de>>(buf: &'de [u8]) -> Result<T> {
    serde_json::from_slice(buf).map_err(|e| format!("JSON deserialization failure: {e}").into())
}

/// Check that a likely user-provided string is non empty
fn assert_non_empty_string(input: impl AsRef<str>, message: impl AsRef<str>) -> Result<String> {
    let input = input.as_ref();
    let message = message.as_ref();
    if input.trim().is_empty() {
        Err(message.into())
    } else {
        Ok(input.trim().to_string())
    }
}