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
//! Reusable functionality related to [WebAssembly Interface types ("WIT")][wit]
//!
//! [wit]: <https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md>
use std::collections::HashMap;
use anyhow::Context;
use serde::ser::SerializeMap;
use serde::{Deserialize, Serialize, Serializer};
use crate::{WitFunction, WitInterface, WitNamespace, WitPackage};
/// Representation of maps (AKA associative arrays) that are usable from WIT
///
/// This representation is required because WIT does not natively
/// have support for a map type, so we must use a list of tuples
pub type WitMap<T> = Vec<(String, T)>;
pub(crate) fn serialize_wit_map<S: Serializer, T>(
map: &WitMap<T>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
T: Serialize,
{
let mut seq = serializer.serialize_map(Some(map.len()))?;
for (key, val) in map {
seq.serialize_entry(key, val)?;
}
seq.end()
}
pub(crate) fn deserialize_wit_map<'de, D: serde::Deserializer<'de>, T>(
deserializer: D,
) -> Result<WitMap<T>, D::Error>
where
T: Deserialize<'de>,
{
let values = HashMap::<String, T>::deserialize(deserializer)?;
Ok(values.into_iter().collect())
}
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
/// Call target identifier, which is equivalent to a WIT specification, which
/// can identify an interface being called and optionally a specific function on that interface.
pub struct CallTargetInterface {
/// WIT namespace (ex. `wasi` in `wasi:keyvalue/readwrite.get`)
pub namespace: String,
/// WIT package name (ex. `keyvalue` in `wasi:keyvalue/readwrite.get`)
pub package: String,
/// WIT interface (ex. `readwrite` in `wasi:keyvalue/readwrite.get`)
pub interface: String,
}
impl CallTargetInterface {
/// Returns the 3-tuple of (namespace, package, interface) for this interface
#[must_use]
pub fn as_parts(&self) -> (&str, &str, &str) {
(&self.namespace, &self.package, &self.interface)
}
/// Build a [`CallTargetInterface`] from constituent parts
#[must_use]
pub fn from_parts((ns, pkg, iface): (&str, &str, &str)) -> Self {
Self {
namespace: ns.into(),
package: pkg.into(),
interface: iface.into(),
}
}
/// Build a target interface from a given operation
pub fn from_operation(operation: impl AsRef<str>) -> anyhow::Result<Self> {
let operation = operation.as_ref();
let (wit_ns, wit_pkg, wit_iface, _) = parse_wit_meta_from_operation(operation)?;
Ok(CallTargetInterface::from_parts((
&wit_ns, &wit_pkg, &wit_iface,
)))
}
}
/// Parse a sufficiently specified WIT operation/method into constituent parts.
///
///
/// # Errors
///
/// Returns `Err` if the operation is not of the form "<package>:<ns>/<interface>.<function>"
///
/// # Example
///
/// ```
/// let (wit_ns, wit_pkg, wit_iface, wit_fn) = parse_wit_meta_from_operation(("wasmcloud:bus/guest-config"));
/// #assert_eq!(wit_ns, "wasmcloud")
/// #assert_eq!(wit_pkg, "bus")
/// #assert_eq!(wit_iface, "iface")
/// #assert_eq!(wit_fn, None)
/// let (wit_ns, wit_pkg, wit_iface, wit_fn) = parse_wit_meta_from_operation(("wasmcloud:bus/guest-config.get"));
/// #assert_eq!(wit_ns, "wasmcloud")
/// #assert_eq!(wit_pkg, "bus")
/// #assert_eq!(wit_iface, "iface")
/// #assert_eq!(wit_fn, Some("get"))
/// ```
pub fn parse_wit_meta_from_operation(
operation: impl AsRef<str>,
) -> anyhow::Result<(WitNamespace, WitPackage, WitInterface, Option<WitFunction>)> {
let operation = operation.as_ref();
let (ns_and_pkg, interface_and_func) = operation
.rsplit_once('/')
.context("failed to parse operation")?;
let (wit_iface, wit_fn) = interface_and_func
.split_once('.')
.context("interface and function should be specified")?;
let (wit_ns, wit_pkg) = ns_and_pkg
.rsplit_once(':')
.context("failed to parse operation for WIT ns/pkg")?;
Ok((
wit_ns.into(),
wit_pkg.into(),
wit_iface.into(),
if wit_fn.is_empty() {
None
} else {
Some(wit_fn.into())
},
))
}