wick_config/
v0.rs

1#![deny(
2  warnings,
3  missing_debug_implementations,
4  trivial_casts,
5  trivial_numeric_casts,
6  unsafe_code,
7  unstable_features,
8  unused_import_braces,
9  unused_qualifications,
10  unreachable_pub,
11  type_alias_bounds,
12  trivial_bounds,
13  mutable_transmutes,
14  invalid_value,
15  explicit_outlives_requirements,
16  deprecated,
17  clashing_extern_declarations,
18  clippy::expect_used,
19  clippy::explicit_deref_methods
20)]
21#![warn(clippy::cognitive_complexity)]
22#![allow(missing_docs, clippy::exhaustive_enums, clippy::exhaustive_structs)]
23
24#[cfg(feature = "config")]
25pub(crate) mod conversions;
26pub(crate) mod parse;
27
28use std::collections::HashMap;
29use std::str::FromStr;
30
31use num_traits::FromPrimitive;
32use serde::{Deserialize, Serialize};
33use serde_json::Value;
34use serde_with_expand_env::with_expand_envs;
35
36#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
37#[serde(deny_unknown_fields)]
38/// A manifest defines the starting state of a Wick host and network.
39pub struct HostManifest {
40  /// The configuration manifest format.
41
42  #[serde(deserialize_with = "with_expand_envs")]
43  pub format: u32,
44  /// The version of the configuration.
45  #[serde(default)]
46  #[serde(deserialize_with = "with_expand_envs")]
47  pub version: String,
48  /// Additional host configuration.
49  #[serde(default)]
50  pub host: Option<HostConfig>,
51  /// The configuration for a Wick network.
52  #[serde(default)]
53  pub network: NetworkManifest,
54  /// The default schematic to execute if none is provided.
55  #[serde(default)]
56  pub default_schematic: Option<String>,
57}
58
59#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
60#[serde(deny_unknown_fields)]
61/// Host configuration options.
62pub struct HostConfig {
63  /// Whether or not to allow the :latest tag on remote artifacts.
64  #[serde(default)]
65  #[serde(deserialize_with = "with_expand_envs")]
66  pub allow_latest: bool,
67  /// A list of registries to connect to insecurely (over HTTP vs HTTPS).
68  #[serde(default)]
69  #[serde(skip_serializing_if = "Vec::is_empty")]
70  pub insecure_registries: Vec<String>,
71  /// The ID for this host, used to identify the host over the mesh.
72  #[serde(default)]
73  pub id: Option<String>,
74  /// The schematics to expose via RPC or the mesh, if any.
75  #[serde(default)]
76  #[serde(skip_serializing_if = "Vec::is_empty")]
77  pub expose: Vec<String>,
78  /// The mesh configuration.
79  #[serde(default)]
80  pub mesh: Option<MeshConfig>,
81  /// Configuration for the GRPC server.
82  #[serde(default)]
83  pub rpc: Option<HttpConfig>,
84  /// Configuration for the HTTP 1 server (development only).
85  #[serde(default)]
86  pub http: Option<HttpConfig>,
87}
88
89#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
90#[serde(deny_unknown_fields)]
91/// Configuration for HTTP/S servers.
92pub struct HttpConfig {
93  /// Enable/disable the server.
94  #[serde(default)]
95  #[serde(deserialize_with = "with_expand_envs")]
96  pub enabled: bool,
97  /// The port to bind to.
98  #[serde(default)]
99  pub port: Option<u16>,
100  /// The address to bind to.
101  #[serde(default)]
102  pub address: Option<String>,
103  /// Path to pem file for TLS.
104  #[serde(default)]
105  pub pem: Option<String>,
106  /// Path to key file for TLS.
107  #[serde(default)]
108  pub key: Option<String>,
109  /// Path to CA file.
110  #[serde(default)]
111  pub ca: Option<String>,
112}
113
114#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
115#[serde(deny_unknown_fields)]
116/// Configuration used to connect to the mesh.
117pub struct MeshConfig {
118  /// Enable/disable the mesh connection.
119  #[serde(default)]
120  #[serde(deserialize_with = "with_expand_envs")]
121  pub enabled: bool,
122  /// The address of the NATS server.
123  #[serde(default)]
124  #[serde(deserialize_with = "with_expand_envs")]
125  pub address: String,
126  /// The path to the NATS credsfile.
127  #[serde(default)]
128  pub creds_path: Option<String>,
129  /// The NATS token.
130  #[serde(default)]
131  pub token: Option<String>,
132}
133
134#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
135#[serde(deny_unknown_fields)]
136/// A Wick network definition.
137pub struct NetworkManifest {
138  /// The unique identifier for this Network.
139  #[serde(default)]
140  pub name: Option<String>,
141  /// The links between capabilities and components.
142  #[serde(default)]
143  #[serde(skip_serializing_if = "Vec::is_empty")]
144  pub schematics: Vec<SchematicManifest>,
145  /// A list of component collections.
146  #[serde(default)]
147  #[serde(skip_serializing_if = "Vec::is_empty")]
148  pub collections: Vec<CollectionDefinition>,
149}
150
151#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
152#[serde(deny_unknown_fields)]
153/// A collection definition.
154pub struct CollectionDefinition {
155  /// The local namespace for the collection.
156  #[serde(default)]
157  #[serde(deserialize_with = "with_expand_envs")]
158  pub namespace: String,
159  /// The kind/type of the collection.
160  #[serde(default)]
161  pub kind: CollectionKind,
162  /// The reference/location of the collection.
163  #[serde(default)]
164  #[serde(deserialize_with = "with_expand_envs")]
165  pub reference: String,
166  /// Data or configuration used to initialize the collection.
167  #[serde(default)]
168  pub data: Option<HashMap<String, Value>>,
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize, Copy, PartialEq)]
172#[serde(deny_unknown_fields)]
173/// Kind of collection.
174pub enum CollectionKind {
175  /// Native collections included at compile-time in a Wick host.
176  Native = 0,
177  /// The URL for a separately managed GRPC endpoint.
178  GrpcUrl = 1,
179  /// A WaPC WebAssembly collection.
180  WaPC = 2,
181  /// A local or remote Network definition.
182  Network = 4,
183}
184
185impl Default for CollectionKind {
186  fn default() -> Self {
187    Self::from_u16(0).unwrap()
188  }
189}
190
191impl FromPrimitive for CollectionKind {
192  fn from_i64(n: i64) -> Option<Self> {
193    Some(match n {
194      0 => Self::Native,
195      1 => Self::GrpcUrl,
196      2 => Self::WaPC,
197      4 => Self::Network,
198      _ => {
199        return None;
200      }
201    })
202  }
203
204  fn from_u64(n: u64) -> Option<Self> {
205    Some(match n {
206      0 => Self::Native,
207      1 => Self::GrpcUrl,
208      2 => Self::WaPC,
209      4 => Self::Network,
210      _ => {
211        return None;
212      }
213    })
214  }
215}
216
217#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
218#[serde(deny_unknown_fields)]
219/// A definition for an individual Wick schematic.
220pub struct SchematicManifest {
221  /// Schematic name.
222  #[serde(deserialize_with = "with_expand_envs")]
223  pub name: String,
224  /// A map from component reference to its target.
225  #[serde(default)]
226  #[serde(skip_serializing_if = "HashMap::is_empty")]
227  #[serde(deserialize_with = "map_component_def")]
228  pub instances: HashMap<String, ComponentDefinition>,
229  /// A list of connections from component to component.
230  #[serde(default)]
231  #[serde(skip_serializing_if = "Vec::is_empty")]
232  #[serde(deserialize_with = "vec_connection")]
233  pub connections: Vec<ConnectionDefinition>,
234  /// A map of constraints and values that limit where this schematic can run.
235  #[serde(default)]
236  #[serde(skip_serializing_if = "HashMap::is_empty")]
237  pub constraints: HashMap<String, String>,
238}
239
240#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
241#[serde(deny_unknown_fields)]
242/// A single component definition.
243pub struct ComponentDefinition {
244  /// The ID of the component (i.e. the alias, key, or namespace).
245  #[serde(deserialize_with = "with_expand_envs")]
246  pub id: String,
247  /// Data to associate with the reference.
248  #[serde(default)]
249  pub data: Option<HashMap<String, Value>>,
250}
251
252#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
253#[serde(deny_unknown_fields)]
254/// A connection between components. This can be specified in short-form syntax (where applicable).
255pub struct ConnectionDefinition {
256  /// The originating component from upstream.
257  #[serde(default)]
258  #[serde(deserialize_with = "connection_target_shortform")]
259  pub from: ConnectionTargetDefinition,
260  /// The destination component (downstream).
261  #[serde(default)]
262  #[serde(deserialize_with = "connection_target_shortform")]
263  pub to: ConnectionTargetDefinition,
264}
265
266#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
267#[serde(deny_unknown_fields)]
268/// A connection target e.g. a port on a reference. This can be specified in short-form syntax (where applicable).
269pub struct ConnectionTargetDefinition {
270  /// The instance name of the referenced component.
271  #[serde(deserialize_with = "with_expand_envs")]
272  pub instance: String,
273  /// The component&#x27;s port.
274  #[serde(deserialize_with = "with_expand_envs")]
275  pub port: String,
276  /// Data to associate with a connection.
277  #[serde(default)]
278  pub data: Option<HashMap<String, Value>>,
279}
280
281impl FromStr for ComponentDefinition {
282  type Err = crate::Error;
283
284  fn from_str(s: &str) -> Result<Self, Self::Err> {
285    Ok(Self {
286      id: s.to_owned(),
287      data: Default::default(),
288    })
289  }
290}
291
292impl FromStr for ConnectionDefinition {
293  type Err = crate::Error;
294
295  fn from_str(s: &str) -> Result<Self, Self::Err> {
296    crate::v0::parse::parse_connection(s)
297  }
298}
299
300impl FromStr for ConnectionTargetDefinition {
301  type Err = crate::Error;
302
303  fn from_str(s: &str) -> Result<Self, Self::Err> {
304    crate::v0::parse::parse_connection_target(s)
305  }
306}
307
308fn map_component_def<'de, D>(deserializer: D) -> Result<HashMap<String, ComponentDefinition>, D::Error>
309where
310  D: serde::Deserializer<'de>,
311{
312  struct ComponentDefinitionVisitor;
313  impl<'de> serde::de::Visitor<'de> for ComponentDefinitionVisitor {
314    type Value = HashMap<String, ComponentDefinition>;
315    fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
316      write!(f, "a map of instances to their components")
317    }
318
319    fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
320    where
321      M: serde::de::MapAccess<'de>,
322    {
323      let mut map = HashMap::with_capacity(access.size_hint().unwrap_or(0));
324
325      while let Some((key, value)) = access.next_entry::<String, serde_value::Value>()? {
326        let result = match value {
327          serde_value::Value::String(s) => {
328            ComponentDefinition::from_str(&s).map_err(|e| serde::de::Error::custom(e.to_string()))?
329          }
330          serde_value::Value::Map(map) => {
331            ComponentDefinition::deserialize(serde_value::ValueDeserializer::new(serde_value::Value::Map(map)))?
332          }
333          _ => {
334            return Err(serde::de::Error::invalid_type(
335              serde::de::Unexpected::Other("other"),
336              &self,
337            ))
338          }
339        };
340
341        map.insert(key, result);
342      }
343
344      Ok(map)
345    }
346  }
347
348  deserializer.deserialize_map(ComponentDefinitionVisitor)
349}
350
351fn vec_connection<'de, D>(deserializer: D) -> Result<Vec<ConnectionDefinition>, D::Error>
352where
353  D: serde::Deserializer<'de>,
354{
355  struct ConnectionDefVisitor;
356  impl<'de> serde::de::Visitor<'de> for ConnectionDefVisitor {
357    type Value = Vec<ConnectionDefinition>;
358    fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
359      write!(f, "a list of connections")
360    }
361
362    fn visit_seq<A: serde::de::SeqAccess<'de>>(self, mut seq: A) -> Result<Vec<ConnectionDefinition>, A::Error> {
363      let mut v = vec![];
364      while let Some(thing) = seq.next_element::<serde_value::Value>()? {
365        let result = match thing {
366          serde_value::Value::String(s) => {
367            ConnectionDefinition::from_str(&s).map_err(|e| serde::de::Error::custom(e.to_string()))?
368          }
369          serde_value::Value::Map(map) => {
370            ConnectionDefinition::deserialize(serde_value::ValueDeserializer::new(serde_value::Value::Map(map)))?
371          }
372          _ => {
373            return Err(serde::de::Error::invalid_type(
374              serde::de::Unexpected::Other("other"),
375              &self,
376            ))
377          }
378        };
379        v.push(result);
380      }
381      Ok(v)
382    }
383  }
384
385  deserializer.deserialize_seq(ConnectionDefVisitor)
386}
387
388fn connection_target_shortform<'de, D>(deserializer: D) -> Result<ConnectionTargetDefinition, D::Error>
389where
390  D: serde::Deserializer<'de>,
391{
392  struct ConnectionTargetVisitor;
393
394  impl<'de> serde::de::Visitor<'de> for ConnectionTargetVisitor {
395    type Value = ConnectionTargetDefinition;
396
397    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
398      formatter.write_str("a connection target definition")
399    }
400
401    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
402    where
403      E: serde::de::Error,
404    {
405      ConnectionTargetDefinition::from_str(s).map_err(|e| serde::de::Error::custom(e.to_string()))
406    }
407
408    fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
409    where
410      A: serde::de::MapAccess<'de>,
411    {
412      ConnectionTargetDefinition::deserialize(serde::de::value::MapAccessDeserializer::new(map))
413    }
414  }
415
416  deserializer.deserialize_any(ConnectionTargetVisitor)
417}