wasm_pkg_common/config/
toml.rs1#![allow(clippy::mutable_key_type)]
3
4use std::collections::HashMap;
5
6use serde::{Deserialize, Serialize};
7
8use crate::{label::Label, package::PackageRef, registry::Registry};
9
10use super::RegistryMapping;
11
12#[derive(Deserialize, Serialize)]
13#[serde(deny_unknown_fields)]
14pub struct TomlConfig {
15 default_registry: Option<Registry>,
16 #[serde(default)]
17 namespace_registries: HashMap<Label, RegistryMapping>,
18 #[serde(default)]
19 package_registry_overrides: HashMap<PackageRef, RegistryMapping>,
20 #[serde(default)]
21 registry: HashMap<Registry, TomlRegistryConfig>,
22}
23
24impl From<TomlConfig> for super::Config {
25 fn from(value: TomlConfig) -> Self {
26 let TomlConfig {
27 default_registry,
28 namespace_registries,
29 package_registry_overrides,
30 registry,
31 } = value;
32
33 let registry_configs = registry
34 .into_iter()
35 .map(|(reg, config)| (reg, config.into()))
36 .collect();
37
38 Self {
39 default_registry,
40 namespace_registries,
41 package_registry_overrides,
42 fallback_namespace_registries: Default::default(),
43 registry_configs,
44 }
45 }
46}
47
48impl From<super::Config> for TomlConfig {
49 fn from(value: super::Config) -> Self {
50 let registry = value
51 .registry_configs
52 .into_iter()
53 .map(|(reg, config)| (reg, config.into()))
54 .collect();
55 Self {
56 default_registry: value.default_registry,
57 namespace_registries: value.namespace_registries,
58 package_registry_overrides: value.package_registry_overrides,
59 registry,
60 }
61 }
62}
63
64#[derive(Deserialize, Serialize)]
65struct TomlRegistryConfig {
66 #[serde(alias = "type")]
67 default: Option<String>,
68 #[serde(flatten)]
69 backend_configs: HashMap<String, toml::Table>,
70}
71
72impl From<TomlRegistryConfig> for super::RegistryConfig {
73 fn from(value: TomlRegistryConfig) -> Self {
74 let TomlRegistryConfig {
75 default,
76 backend_configs,
77 } = value;
78 Self {
79 default_backend: default,
80 backend_configs,
81 }
82 }
83}
84
85impl From<super::RegistryConfig> for TomlRegistryConfig {
86 fn from(value: super::RegistryConfig) -> Self {
87 let super::RegistryConfig {
88 default_backend: backend_default,
89 backend_configs,
90 } = value;
91 Self {
92 default: backend_default,
93 backend_configs,
94 }
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn smoke_test() {
104 let toml_config = toml::toml! {
105 default_registry = "example.com"
106
107 [namespace_registries]
108 wasi = "wasi.dev"
109
110 [package_registry_overrides]
111 "example:foo" = "example.com"
112
113 [registry."wasi.dev".oci]
114 auth = { username = "open", password = "sesame" }
115
116 [registry."example.com"]
117 type = "test"
118 test = { token = "top_secret" }
119 };
120 let wasi_dev: Registry = "wasi.dev".parse().unwrap();
121 let example_com: Registry = "example.com".parse().unwrap();
122
123 let toml_cfg: TomlConfig = toml_config.try_into().unwrap();
124 let cfg = crate::config::Config::from(toml_cfg);
125
126 assert_eq!(cfg.default_registry(), Some(&example_com));
127 assert_eq!(
128 cfg.resolve_registry(&"wasi:http".parse().unwrap()),
129 Some(&wasi_dev)
130 );
131 assert_eq!(
132 cfg.resolve_registry(&"example:foo".parse().unwrap()),
133 Some(&example_com)
134 );
135
136 #[derive(Deserialize)]
137 struct TestConfig {
138 token: String,
139 }
140 let test_cfg: TestConfig = cfg
141 .registry_config(&example_com)
142 .unwrap()
143 .backend_config("test")
144 .unwrap()
145 .unwrap();
146 assert_eq!(test_cfg.token, "top_secret");
147 }
148
149 #[test]
150 fn type_parses_correctly() {
151 let toml_config = toml::toml! {
152 [namespace_registries]
153 test = "localhost:1234"
154
155 [package_registry_overrides]
156
157 [registry."localhost:1234".warg]
158 config_file = "/a/path"
159 };
160
161 let toml_cfg: TomlConfig = toml_config.try_into().unwrap();
162 let cfg = crate::config::Config::from(toml_cfg);
163 let reg_conf = cfg
164 .registry_config(&"localhost:1234".parse().unwrap())
165 .expect("Should have config for registry");
166 assert_eq!(
167 reg_conf
168 .default_backend()
169 .expect("Should have a default set"),
170 "warg"
171 );
172
173 let toml_config = toml::toml! {
174 [namespace_registries]
175 test = "localhost:1234"
176
177 [package_registry_overrides]
178
179 [registry."localhost:1234".warg]
180 config_file = "/a/path"
181 [registry."localhost:1234".oci]
182 auth = { username = "open", password = "sesame" }
183 };
184
185 let toml_cfg: TomlConfig = toml_config.try_into().unwrap();
186 let cfg = crate::config::Config::from(toml_cfg);
187 let reg_conf = cfg
188 .registry_config(&"localhost:1234".parse().unwrap())
189 .expect("Should have config for registry");
190 assert!(
191 reg_conf.default_backend().is_none(),
192 "Should not have a type set when two configs exist"
193 );
194
195 let toml_config = toml::toml! {
196 [namespace_registries]
197 test = "localhost:1234"
198
199 [package_registry_overrides]
200
201 [registry."localhost:1234"]
202 type = "foobar"
203 [registry."localhost:1234".warg]
204 config_file = "/a/path"
205 [registry."localhost:1234".oci]
206 auth = { username = "open", password = "sesame" }
207 };
208
209 let toml_cfg: TomlConfig = toml_config.try_into().unwrap();
210 let cfg = crate::config::Config::from(toml_cfg);
211 let reg_conf = cfg
212 .registry_config(&"localhost:1234".parse().unwrap())
213 .expect("Should have config for registry");
214 assert_eq!(
215 reg_conf
216 .default_backend()
217 .expect("Should have a default set using the type alias"),
218 "foobar"
219 );
220
221 let toml_config = toml::toml! {
222 [namespace_registries]
223 test = "localhost:1234"
224
225 [registry."localhost:1234"]
226 default = "foobar"
227 [registry."localhost:1234".warg]
228 config_file = "/a/path"
229 [registry."localhost:1234".oci]
230 auth = { username = "open", password = "sesame" }
231 };
232
233 let toml_cfg: TomlConfig = toml_config.try_into().unwrap();
234 let cfg = crate::config::Config::from(toml_cfg);
235 let reg_conf = cfg
236 .registry_config(&"localhost:1234".parse().unwrap())
237 .expect("Should have config for registry");
238 assert_eq!(
239 reg_conf
240 .default_backend()
241 .expect("Should have a default set"),
242 "foobar"
243 );
244 }
245
246 #[test]
247 fn test_custom_namespace_config() {
248 let toml_config = toml::toml! {
249 [namespace_registries]
250 test = { registry = "localhost", metadata = { preferredProtocol = "oci", "oci" = {registry = "ghcr.io", namespacePrefix = "webassembly/" } } }
251 foo = "foo:1234"
252
253 [package_registry_overrides]
254 "foo:bar" = { registry = "localhost", metadata = { preferredProtocol = "oci", "oci" = {registry = "ghcr.io", namespacePrefix = "webassembly/" } } }
255
256 [registry."localhost".oci]
257 auth = { username = "open", password = "sesame" }
258 };
259
260 let toml_cfg: TomlConfig = toml_config.try_into().unwrap();
261 let cfg = crate::config::Config::from(toml_cfg);
262
263 let ns_config = cfg
265 .namespace_registry(&"foo".parse().unwrap())
266 .expect("Should have a namespace config");
267 let reg = match ns_config {
268 RegistryMapping::Registry(r) => r,
269 _ => panic!("Should have a registry namespace config"),
270 };
271 assert_eq!(
272 reg,
273 &"foo:1234".parse::<Registry>().unwrap(),
274 "Should have a registry"
275 );
276
277 let ns_config = cfg
278 .namespace_registry(&"test".parse().unwrap())
279 .expect("Should have a namespace config");
280 let custom = match ns_config {
281 RegistryMapping::Custom(c) => c,
282 _ => panic!("Should have a custom namespace config"),
283 };
284 assert_eq!(
285 custom.registry,
286 "localhost".parse().unwrap(),
287 "Should have a registry"
288 );
289 assert_eq!(
290 custom.metadata.preferred_protocol(),
291 Some("oci"),
292 "Should have a preferred protocol"
293 );
294 let map = custom
296 .metadata
297 .protocol_configs
298 .get("oci")
299 .expect("Should have a protocol config");
300 assert_eq!(
301 map.get("registry").expect("registry should exist"),
302 "ghcr.io",
303 "Should have a registry"
304 );
305 assert_eq!(
306 map.get("namespacePrefix")
307 .expect("namespacePrefix should exist"),
308 "webassembly/",
309 "Should have a namespace prefix"
310 );
311
312 let ns_config = cfg
314 .package_registry_override(&"foo:bar".parse().unwrap())
315 .expect("Should have a package override config");
316 let custom = match ns_config {
317 RegistryMapping::Custom(c) => c,
318 _ => panic!("Should have a custom namespace config"),
319 };
320 assert_eq!(
321 custom.registry,
322 "localhost".parse().unwrap(),
323 "Should have a registry"
324 );
325 assert_eq!(
326 custom.metadata.preferred_protocol(),
327 Some("oci"),
328 "Should have a preferred protocol"
329 );
330 let map = custom
332 .metadata
333 .protocol_configs
334 .get("oci")
335 .expect("Should have a protocol config");
336 assert_eq!(
337 map.get("registry").expect("registry should exist"),
338 "ghcr.io",
339 "Should have a registry"
340 );
341 assert_eq!(
342 map.get("namespacePrefix")
343 .expect("namespacePrefix should exist"),
344 "webassembly/",
345 "Should have a namespace prefix"
346 );
347 }
348}