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
131
132
133
134
135
136
137
138
139
//! Core reusable functionality related to [WebAssembly Interface types ("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};

// I don't know if these would be generated or if we'd just include them in the library and then use them in the generated code, but they work around the lack of a map type in wit

/// 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,
    // TODO(brooksmtownsend): I'm almost certain we do not need this.
    /// WIT package name (ex. `get` in `wasi:keyvalue/readwrite.get`)
    pub function: Option<String>,
}

impl CallTargetInterface {
    /// Returns the 3-tuple of (namespace, package, interface) for this interface
    #[must_use]
    pub fn as_parts(&self) -> (&str, &str, &str, Option<&str>) {
        (
            &self.namespace,
            &self.package,
            &self.interface,
            self.function.as_deref(),
        )
    }

    /// Build a [`TargetInterface`] from constituent parts
    #[must_use]
    pub fn from_parts(parts: (&str, &str, &str, Option<&str>)) -> Self {
        let (ns, pkg, iface, function) = parts;
        Self {
            namespace: ns.into(),
            package: pkg.into(),
            interface: iface.into(),
            function: function.map(String::from),
        }
    }

    /// 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, wit_fn) = parse_wit_meta_from_operation(operation)?;
        Ok(CallTargetInterface::from_parts((
            &wit_ns,
            &wit_pkg,
            &wit_iface,
            wit_fn.as_deref(),
        )))
    }
}

/// 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())
        },
    ))
}