1use std::collections::HashMap;
6
7use anyhow::{bail, Context as _, Result};
8use semver::Version;
9use serde::ser::SerializeMap;
10use serde::{Deserialize, Serialize, Serializer};
11
12use crate::{WitFunction, WitInterface, WitNamespace, WitPackage};
13
14pub type WitMap<T> = Vec<(String, T)>;
19
20pub(crate) fn serialize_wit_map<S: Serializer, T>(
21 map: &WitMap<T>,
22 serializer: S,
23) -> std::result::Result<S::Ok, S::Error>
24where
25 T: Serialize,
26{
27 let mut seq = serializer.serialize_map(Some(map.len()))?;
28 for (key, val) in map {
29 seq.serialize_entry(key, val)?;
30 }
31 seq.end()
32}
33
34pub(crate) fn deserialize_wit_map<'de, D: serde::Deserializer<'de>, T>(
35 deserializer: D,
36) -> std::result::Result<WitMap<T>, D::Error>
37where
38 T: Deserialize<'de>,
39{
40 let values = HashMap::<String, T>::deserialize(deserializer)?;
41 Ok(values.into_iter().collect())
42}
43
44#[derive(Debug, Clone, Eq, Hash, PartialEq)]
45pub struct CallTargetInterface {
48 pub namespace: String,
50 pub package: String,
52 pub interface: String,
54}
55
56impl CallTargetInterface {
57 #[must_use]
59 pub fn as_parts(&self) -> (&str, &str, &str) {
60 (&self.namespace, &self.package, &self.interface)
61 }
62
63 pub fn as_instance(&self) -> String {
65 format!("{}:{}/{}", self.namespace, self.package, self.interface)
66 }
67
68 #[must_use]
70 pub fn from_parts((ns, pkg, iface): (&str, &str, &str)) -> Self {
71 Self {
72 namespace: ns.into(),
73 package: pkg.into(),
74 interface: iface.into(),
75 }
76 }
77
78 pub fn from_operation(operation: impl AsRef<str>) -> Result<Self> {
80 let operation = operation.as_ref();
81 let (wit_ns, wit_pkg, wit_iface, _) = parse_wit_meta_from_operation(operation)?;
82 Ok(CallTargetInterface::from_parts((
83 &wit_ns, &wit_pkg, &wit_iface,
84 )))
85 }
86}
87
88pub fn parse_wit_meta_from_operation(
111 operation: impl AsRef<str>,
112) -> Result<(WitNamespace, WitPackage, WitInterface, Option<WitFunction>)> {
113 let operation = operation.as_ref();
114 let (ns_and_pkg, interface_and_func) = operation
115 .rsplit_once('/')
116 .context("failed to parse operation")?;
117 let (wit_ns, wit_pkg) = ns_and_pkg
118 .rsplit_once(':')
119 .context("failed to parse operation for WIT ns/pkg")?;
120 let (wit_iface, wit_fn) = match interface_and_func.split_once('.') {
121 Some((iface, func)) => (iface, Some(func.to_string())),
122 None => (interface_and_func, None),
123 };
124 Ok((wit_ns.into(), wit_pkg.into(), wit_iface.into(), wit_fn))
125}
126
127type WitInformationTuple = (
128 WitNamespace,
129 Vec<WitPackage>,
130 Option<Vec<WitInterface>>,
131 Option<WitFunction>,
132 Option<Version>,
133);
134
135pub fn parse_wit_package_name(p: impl AsRef<str>) -> Result<WitInformationTuple> {
190 let p = p.as_ref();
191 let (rest, version) = match p.rsplit_once('@') {
193 Some((rest, version)) => (
194 rest,
195 Some(
196 Version::parse(version)
197 .map_err(|e| anyhow::anyhow!(e))
198 .with_context(|| {
199 format!("failed to parse version from wit package name [{p}]")
200 })?,
201 ),
202 ),
203 None => (p, None),
204 };
205
206 let (ns_and_pkg, interface_and_func) = match rest.rsplit_once('/') {
208 Some((ns_and_pkg, interface_and_func)) => (ns_and_pkg, Some(interface_and_func)),
209 None => (rest, None),
210 };
211
212 let ns_pkg_split = ns_and_pkg.split(':').collect::<Vec<&str>>();
214 let (ns, packages) = match ns_pkg_split[..] {
215 [] => bail!("invalid package name, missing namespace & package"),
216 [_] => bail!("invalid package name, invalid package"),
217 [ns, ref packages @ ..] => (ns, packages),
218 };
219
220 let (mut interfaces, iface_with_fn) = match interface_and_func
222 .unwrap_or_default()
223 .split('/')
224 .filter(|v| !v.is_empty())
225 .collect::<Vec<&str>>()[..]
226 {
227 [] => (None, None),
228 [iface] => (Some(vec![]), Some(iface)),
229 [iface, f] => (Some(vec![iface]), Some(f)),
230 [ref ifaces @ .., f] => (Some(Vec::from(ifaces)), Some(f)),
231 };
232
233 let func = match iface_with_fn {
234 Some(iface_with_fn) => match iface_with_fn.split_once('.') {
235 Some((iface, f)) => {
236 if let Some(ref mut interfaces) = interfaces {
237 interfaces.push(iface);
238 };
239 Some(f)
240 }
241 None => {
242 if let Some(ref mut interfaces) = interfaces {
243 interfaces.push(iface_with_fn);
244 };
245 None
246 }
247 },
248 None => None,
249 };
250
251 Ok((
252 ns.into(),
253 packages
254 .iter()
255 .map(|v| String::from(*v))
256 .collect::<Vec<_>>(),
257 interfaces.map(|v| v.into_iter().map(String::from).collect::<Vec<_>>()),
258 func.map(String::from),
259 version,
260 ))
261}
262
263#[cfg(test)]
265mod test {
266 use semver::Version;
267
268 use super::parse_wit_package_name;
269 #[test]
270 fn test_parse_wit_package_name() {
271 let (ns, packages, interfaces, func, version) =
272 parse_wit_package_name("wasi:http").expect("should have parsed'wasi:http'");
273 assert_eq!(ns, "wasi".to_string());
274 assert_eq!(packages, vec!["http".to_string()]);
275 assert_eq!(interfaces, None);
276 assert_eq!(func, None);
277 assert_eq!(version, None);
278
279 let (ns, packages, interfaces, func, version) = parse_wit_package_name("wasi:http@0.2.2")
280 .expect("should have parsed 'wasi:http@0.2.2'");
281 assert_eq!(ns, "wasi".to_string());
282 assert_eq!(packages, vec!["http".to_string()]);
283 assert_eq!(interfaces, None);
284 assert_eq!(func, None);
285 assert_eq!(version, Version::parse("0.2.2").ok());
286
287 let (ns, packages, interfaces, func, version) =
288 parse_wit_package_name("wasmcloud:bus/guest-config")
289 .expect("should have parsed 'wasmcloud:bus/guest-config'");
290 assert_eq!(ns, "wasmcloud");
291 assert_eq!(packages, vec!["bus".to_string()]);
292 assert_eq!(interfaces, Some(vec!["guest-config".to_string()]));
293 assert_eq!(func, None);
294 assert_eq!(version, None);
295
296 let (ns, packages, interfaces, func, version) =
297 parse_wit_package_name("wasmcloud:bus/guest-config.get")
298 .expect("should have parsed 'wasmcloud:bus/guest-config.get'");
299 assert_eq!(ns, "wasmcloud");
300 assert_eq!(packages, vec!["bus".to_string()]);
301 assert_eq!(interfaces, Some(vec!["guest-config".to_string()]));
302 assert_eq!(func, Some("get".to_string()));
303 assert_eq!(version, None);
304
305 let (ns, packages, interfaces, func, version) =
306 parse_wit_package_name("wasi:http/incoming-handler@0.2.0")
307 .expect("should have parsed 'wasi:http/incoming-handler@0.2.0'");
308 assert_eq!(ns, "wasi".to_string());
309 assert_eq!(packages, vec!["http".to_string()]);
310 assert_eq!(interfaces, Some(vec!["incoming-handler".to_string()]));
311 assert_eq!(func, None);
312 assert_eq!(version, Version::parse("0.2.0").ok());
313
314 let (ns, packages, interfaces, func, version) =
315 parse_wit_package_name("wasi:keyvalue/atomics.increment@0.2.0-draft")
316 .expect("should have parsed 'wasi:keyvalue/atomics.increment@0.2.0-draft'");
317 assert_eq!(ns, "wasi".to_string());
318 assert_eq!(packages, vec!["keyvalue".to_string()]);
319 assert_eq!(interfaces, Some(vec!["atomics".to_string()]));
320 assert_eq!(func, Some("increment".to_string()));
321 assert_eq!(version, Version::parse("0.2.0-draft").ok());
322 }
323}