1#![allow(missing_docs)] pub mod components;
5
6pub(crate) mod app_config;
7mod cache;
8pub(crate) mod common;
9pub(crate) mod component_config;
10pub(crate) mod configuration_tree;
11pub(crate) mod import_cache;
12pub(crate) mod lockdown_config;
13pub(crate) mod permissions;
14pub(crate) mod test_config;
15pub(crate) mod types_config;
16
17use std::collections::HashMap;
18use std::path::{Path, PathBuf};
19
20pub use app_config::*;
21use asset_container::Asset;
22pub use bindings::BoundIdentifier;
23pub use common::*;
24pub use component_config::*;
25pub use configuration_tree::*;
26pub use lockdown_config::*;
27pub use permissions::{Permissions, PermissionsBuilder};
28pub use test_config::*;
29use tracing::debug;
30pub use types_config::*;
31use wick_asset_reference::{AssetReference, FetchOptions};
32use wick_interface_types::Field;
33use wick_packet::validation::expect_configuration_matches;
34use wick_packet::{Entity, RuntimeConfig};
35
36use self::template_config::Renderable;
37use crate::load::resolve_configuration;
38use crate::lockdown::Lockdown;
39use crate::{Error, Imports, RootConfig};
40
41#[derive(Debug, Clone, property::Property)]
42#[property(get(public), set(public), mut(public, suffix = "_mut"))]
43pub struct UninitializedConfiguration {
45 pub(crate) manifest: WickConfiguration,
47 pub(crate) root_config: Option<RuntimeConfig>,
49 pub(crate) env: Option<HashMap<String, String>>,
51 pub(crate) lockdown_config: Option<LockdownConfiguration>,
53}
54
55impl UninitializedConfiguration {
56 #[must_use]
57 pub const fn new(manifest: WickConfiguration) -> Self {
59 Self {
60 manifest,
61 root_config: None,
62 env: None,
63 lockdown_config: None,
64 }
65 }
66
67 #[must_use]
69 #[allow(clippy::missing_const_for_fn)]
70 pub fn into_inner(self) -> WickConfiguration {
71 self.manifest
72 }
73
74 pub fn finish(mut self) -> Result<WickConfiguration, Error> {
76 debug!(root_config=?self.root_config, env=?self.env.as_ref().map(|c|format!("{} variables",c.len())), "initializing configuration");
77
78 expect_configuration_matches(
79 self
80 .manifest
81 .source()
82 .map_or("<unknown>", |p| p.to_str().unwrap_or("<invalid>")),
83 self.root_config.as_ref(),
84 self.manifest.config(),
85 )
86 .map_err(Error::ConfigurationInvalid)?;
87 self.manifest.set_env(self.env);
88 self.manifest.set_root_config(self.root_config);
89 self.manifest.initialize()?;
90 self.manifest.validate()?;
91 Ok(self.manifest)
92 }
93}
94
95impl Imports for UninitializedConfiguration {
96 fn imports(&self) -> &[Binding<ImportDefinition>] {
97 self.manifest.imports()
98 }
99}
100
101impl Lockdown for UninitializedConfiguration {
102 fn lockdown(&self, id: Option<&str>, lockdown: &LockdownConfiguration) -> Result<(), crate::lockdown::LockdownError> {
103 self.manifest.lockdown(id, lockdown)?;
104 Ok(())
105 }
106}
107
108#[derive(Debug, Clone, derive_asset_container::AssetManager, serde::Serialize)]
110#[asset(asset(AssetReference))]
111#[serde(untagged)]
112
113pub enum WickConfiguration {
114 Component(ComponentConfiguration),
116 App(AppConfiguration),
118 Types(TypesConfiguration),
120 Tests(TestConfiguration),
122 Lockdown(LockdownConfiguration),
124}
125
126impl WickConfiguration {
127 pub async fn fetch_tree(
145 path: impl Into<AssetReference> + Send,
146 root_config: Option<RuntimeConfig>,
147 root_env: Option<HashMap<String, String>>,
148 options: FetchOptions,
149 ) -> Result<ConfigurationTreeNode<WickConfiguration>, Error> {
150 let mut config = Self::fetch(path, options.clone()).await?;
151 let source = config.manifest.source().map(ToOwned::to_owned);
152 config
153 .manifest
154 .render_config(source.as_deref(), root_config.as_ref(), root_env.as_ref())?;
155
156 let renderer = |runtime_config: Option<RuntimeConfig>, mut child: UninitializedConfiguration| {
157 child.set_root_config(runtime_config);
158 child.finish()
159 };
160
161 let children = fetch_children(&config, options, &renderer).await?;
162 Ok(ConfigurationTreeNode::new(
163 Entity::LOCAL.into(),
164 config.finish()?,
165 children,
166 ))
167 }
168
169 pub async fn fetch_uninitialized_tree(
185 path: impl Into<AssetReference> + Send,
186 options: FetchOptions,
187 ) -> Result<ConfigurationTreeNode<UninitializedConfiguration>, Error> {
188 let config = Self::fetch(path, options.clone()).await?;
189
190 let children = fetch_children(&config, options, &|_, b| Ok(b)).await?;
191
192 Ok(ConfigurationTreeNode::new(Entity::LOCAL.into(), config, children))
193 }
194
195 pub async fn fetch(
212 asset: impl Into<AssetReference> + Send,
213 options: FetchOptions,
214 ) -> Result<UninitializedConfiguration, Error> {
215 let asset: AssetReference = asset.into();
216
217 let cache_result = cache::CONFIG_CACHE.lock().get(&asset).cloned();
218
219 let config = if let Some(config) = cache_result {
220 tracing::trace!(cache_hit = true, asset=%asset.location(), "config::fetch");
221 config
222 } else {
223 tracing::trace!(cache_hit = false, asset=%asset.location(), "config::fetch");
224 let bytes = asset.fetch(options.clone()).await?;
225 let source = asset.path().unwrap_or_else(|e| PathBuf::from(format!("<ERROR:{}>", e)));
226 let config = WickConfiguration::load_from_bytes(&bytes, &Some(source))?;
227 config.manifest.update_baseurls();
228 match &config.manifest {
229 WickConfiguration::Component(c) => {
230 c.setup_cache(options).await?;
231 }
232 WickConfiguration::App(c) => {
233 c.setup_cache(options).await?;
234 }
235 WickConfiguration::Types(_) => {}
236 WickConfiguration::Tests(_) => {}
237 WickConfiguration::Lockdown(_) => {}
238 }
239
240 cache::CONFIG_CACHE.lock().insert(asset.clone(), config.clone());
241 config
242 };
243
244 Ok(config)
245 }
246
247 pub fn load_from_bytes(bytes: &[u8], source: &Option<PathBuf>) -> Result<UninitializedConfiguration, Error> {
265 let string = &String::from_utf8(bytes.to_vec()).map_err(|_| Error::Utf8)?;
266
267 resolve_configuration(string, source)
268 }
269
270 pub fn from_yaml(src: &str, source: &Option<PathBuf>) -> Result<UninitializedConfiguration, Error> {
289 resolve_configuration(src, source)
290 }
291
292 #[cfg(feature = "v1")]
311 pub fn into_v1_yaml(self) -> Result<String, Error> {
312 Ok(serde_yaml::to_string(&self.into_v1()?).unwrap())
313 }
314
315 #[cfg(feature = "v1")]
334 pub fn into_v1_json(self) -> Result<serde_json::Value, Error> {
335 Ok(serde_json::to_value(&self.into_v1()?).unwrap())
336 }
337
338 #[cfg(feature = "v1")]
339 fn into_v1(self) -> Result<crate::v1::WickConfig, Error> {
340 match self {
341 WickConfiguration::Component(c) => Ok(crate::v1::WickConfig::ComponentConfiguration(c.try_into()?)),
342 WickConfiguration::App(c) => Ok(crate::v1::WickConfig::AppConfiguration(c.try_into()?)),
343 WickConfiguration::Types(c) => Ok(crate::v1::WickConfig::TypesConfiguration(c.try_into()?)),
344 WickConfiguration::Tests(c) => Ok(crate::v1::WickConfig::TestConfiguration(c.try_into()?)),
345 WickConfiguration::Lockdown(c) => Ok(crate::v1::WickConfig::LockdownConfiguration(c.try_into()?)),
346 }
347 }
348
349 #[must_use]
351 pub fn name(&self) -> Option<&str> {
352 match self {
353 WickConfiguration::Component(v) => v.name().map(|s| s.as_str()),
354 WickConfiguration::App(v) => Some(v.name()),
355 WickConfiguration::Types(v) => v.name().map(|s| s.as_str()),
356 WickConfiguration::Tests(v) => v.name().map(|s| s.as_str()),
357 WickConfiguration::Lockdown(_) => None,
358 }
359 }
360
361 #[must_use]
363 pub fn metadata(&self) -> Option<&Metadata> {
364 match self {
365 WickConfiguration::Component(v) => v.metadata(),
366 WickConfiguration::App(v) => v.metadata(),
367 WickConfiguration::Types(v) => v.metadata(),
368 WickConfiguration::Tests(_v) => None,
369 WickConfiguration::Lockdown(_v) => None,
370 }
371 }
372
373 pub fn validate(&self) -> Result<(), Error> {
375 match self {
376 WickConfiguration::Component(v) => v.validate(),
377 WickConfiguration::App(v) => v.validate(),
378 WickConfiguration::Types(v) => v.validate(),
379 WickConfiguration::Tests(v) => v.validate(),
380 WickConfiguration::Lockdown(v) => v.validate(),
381 }
382 }
383
384 #[must_use]
386 fn config(&self) -> &[Field] {
387 match self {
388 WickConfiguration::Component(v) => v.config(),
389 WickConfiguration::App(_v) => Default::default(),
390 WickConfiguration::Types(_v) => Default::default(),
391 WickConfiguration::Tests(_v) => Default::default(),
392 WickConfiguration::Lockdown(_v) => Default::default(),
393 }
394 }
395
396 fn set_env(&mut self, env: Option<HashMap<String, String>>) -> &mut Self {
398 match self {
399 WickConfiguration::App(ref mut v) => {
400 v.env = env;
401 }
402 WickConfiguration::Component(_) => (),
403 WickConfiguration::Types(_) => (),
404 WickConfiguration::Tests(_) => (),
405 WickConfiguration::Lockdown(v) => v.env = env,
406 }
407 self
408 }
409
410 pub const fn kind(&self) -> ConfigurationKind {
412 match self {
413 WickConfiguration::Component(_) => ConfigurationKind::Component,
414 WickConfiguration::App(_) => ConfigurationKind::App,
415 WickConfiguration::Types(_) => ConfigurationKind::Types,
416 WickConfiguration::Tests(_) => ConfigurationKind::Tests,
417 WickConfiguration::Lockdown(_) => ConfigurationKind::Lockdown,
418 }
419 }
420
421 #[must_use]
423 pub fn resources(&self) -> &[Binding<ResourceDefinition>] {
424 match self {
425 WickConfiguration::Component(c) => c.resources(),
426 WickConfiguration::App(c) => c.resources(),
427 WickConfiguration::Types(_) => &[],
428 WickConfiguration::Tests(_) => &[],
429 WickConfiguration::Lockdown(_) => &[],
430 }
431 }
432
433 #[must_use]
435 pub fn version(&self) -> Option<&str> {
436 match self {
437 WickConfiguration::Component(v) => v.version(),
438 WickConfiguration::App(v) => v.version(),
439 WickConfiguration::Types(v) => v.version(),
440 WickConfiguration::Tests(_) => None,
441 WickConfiguration::Lockdown(_) => None,
442 }
443 }
444
445 #[must_use]
447 pub fn package(&self) -> Option<&PackageConfig> {
448 match self {
449 WickConfiguration::Component(v) => v.package(),
450 WickConfiguration::App(v) => v.package(),
451 WickConfiguration::Types(v) => v.package(),
452 WickConfiguration::Tests(_) => None,
453 WickConfiguration::Lockdown(_) => None,
454 }
455 }
456
457 #[allow(clippy::missing_const_for_fn)]
459 pub fn try_component_config(self) -> Result<ComponentConfiguration, Error> {
460 match self {
461 WickConfiguration::Component(v) => Ok(v),
462 _ => Err(Error::UnexpectedConfigurationKind(
463 ConfigurationKind::Component,
464 self.kind(),
465 )),
466 }
467 }
468
469 #[allow(clippy::missing_const_for_fn)]
471 pub fn try_app_config(self) -> Result<AppConfiguration, Error> {
472 match self {
473 WickConfiguration::App(v) => Ok(v),
474 _ => Err(Error::UnexpectedConfigurationKind(ConfigurationKind::App, self.kind())),
475 }
476 }
477
478 #[allow(clippy::missing_const_for_fn)]
480 pub fn try_test_config(self) -> Result<TestConfiguration, Error> {
481 match self {
482 WickConfiguration::Tests(v) => Ok(v),
483 _ => Err(Error::UnexpectedConfigurationKind(
484 ConfigurationKind::Tests,
485 self.kind(),
486 )),
487 }
488 }
489
490 #[allow(clippy::missing_const_for_fn)]
492 pub fn try_types_config(self) -> Result<TypesConfiguration, Error> {
493 match self {
494 WickConfiguration::Types(v) => Ok(v),
495 _ => Err(Error::UnexpectedConfigurationKind(
496 ConfigurationKind::Types,
497 self.kind(),
498 )),
499 }
500 }
501
502 #[allow(clippy::missing_const_for_fn)]
504 pub fn try_lockdown_config(self) -> Result<LockdownConfiguration, Error> {
505 match self {
506 WickConfiguration::Lockdown(v) => Ok(v),
507 _ => Err(Error::UnexpectedConfigurationKind(
508 ConfigurationKind::Lockdown,
509 self.kind(),
510 )),
511 }
512 }
513
514 fn initialize(&mut self) -> Result<&Self, Error> {
516 match self {
517 WickConfiguration::Component(v) => {
518 v.initialize()?;
519 }
520 WickConfiguration::App(v) => {
521 v.initialize()?;
522 }
523 WickConfiguration::Types(_) => (),
524 WickConfiguration::Tests(v) => {
525 v.initialize()?;
526 }
527 WickConfiguration::Lockdown(v) => {
528 v.initialize()?;
529 }
530 }
531 self.update_baseurls();
532 Ok(self)
533 }
534
535 pub fn set_source(&mut self, src: &Path) {
537 match self {
538 WickConfiguration::Component(v) => v.set_source(src),
539 WickConfiguration::App(v) => v.set_source(src),
540 WickConfiguration::Types(v) => v.set_source(src),
541 WickConfiguration::Tests(v) => v.set_source(src),
542 WickConfiguration::Lockdown(v) => v.set_source(src),
543 }
544 }
545
546 fn update_baseurls(&self) {
547 match self {
548 WickConfiguration::Component(v) => v.update_baseurls(),
549 WickConfiguration::App(v) => v.update_baseurls(),
550 WickConfiguration::Types(v) => v.update_baseurls(),
551 WickConfiguration::Tests(v) => v.update_baseurls(),
552 WickConfiguration::Lockdown(v) => v.update_baseurls(),
553 }
554 }
555
556 #[must_use]
558 pub fn source(&self) -> Option<&Path> {
559 match self {
560 WickConfiguration::Component(v) => v.source.as_deref(),
561 WickConfiguration::App(v) => v.source.as_deref(),
562 WickConfiguration::Types(v) => v.source.as_deref(),
563 WickConfiguration::Tests(v) => v.source.as_deref(),
564 WickConfiguration::Lockdown(v) => v.source.as_deref(),
565 }
566 }
567}
568
569impl Renderable for WickConfiguration {
570 fn render_config(
571 &mut self,
572 source: Option<&Path>,
573 root_config: Option<&RuntimeConfig>,
574 env: Option<&HashMap<String, String>>,
575 ) -> Result<(), crate::error::ManifestError> {
576 match self {
577 WickConfiguration::Component(c) => c.render_config(source, root_config, env),
578 WickConfiguration::App(c) => c.render_config(source, root_config, env),
579 WickConfiguration::Types(c) => c.render_config(source, root_config, env),
580 WickConfiguration::Tests(c) => c.render_config(source, root_config, env),
581 WickConfiguration::Lockdown(c) => c.render_config(source, root_config, env),
582 }
583 }
584}
585
586impl Lockdown for WickConfiguration {
587 fn lockdown(&self, id: Option<&str>, lockdown: &LockdownConfiguration) -> Result<(), crate::lockdown::LockdownError> {
588 match self {
589 WickConfiguration::Component(v) => v.lockdown(id, lockdown),
590 WickConfiguration::App(v) => v.lockdown(id, lockdown),
591 WickConfiguration::Types(_) => Ok(()),
592 WickConfiguration::Tests(_) => Ok(()),
593 WickConfiguration::Lockdown(_) => Ok(()),
594 }
595 }
596}
597
598impl Imports for WickConfiguration {
599 fn imports(&self) -> &[Binding<ImportDefinition>] {
600 match self {
601 WickConfiguration::Component(c) => c.import(),
602 WickConfiguration::App(c) => c.import(),
603 WickConfiguration::Types(_) => &[],
604 WickConfiguration::Tests(_) => &[],
605 WickConfiguration::Lockdown(_) => &[],
606 }
607 }
608}
609
610impl RootConfig for WickConfiguration {
611 fn root_config(&self) -> Option<&RuntimeConfig> {
612 match self {
613 WickConfiguration::App(v) => v.root_config.as_ref(),
614 WickConfiguration::Component(v) => v.root_config.as_ref(),
615 WickConfiguration::Types(_) => None,
616 WickConfiguration::Tests(_) => None,
617 WickConfiguration::Lockdown(_) => None,
618 }
619 }
620
621 fn set_root_config(&mut self, config: Option<RuntimeConfig>) {
622 match self {
623 WickConfiguration::App(v) => {
624 v.root_config = config;
625 }
626 WickConfiguration::Component(v) => {
627 v.root_config = config;
628 }
629 WickConfiguration::Types(_) => (),
630 WickConfiguration::Tests(_) => (),
631 WickConfiguration::Lockdown(_) => (),
632 }
633 }
634}
635
636#[derive(Debug, Clone, Copy)]
637#[must_use]
639
640pub enum ConfigurationKind {
641 App,
643 Component,
645 Types,
647 Tests,
649 Lockdown,
651}
652
653impl std::fmt::Display for ConfigurationKind {
654 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
655 match self {
656 ConfigurationKind::App => write!(f, "wick/app"),
657 ConfigurationKind::Component => write!(f, "wick/component"),
658 ConfigurationKind::Types => write!(f, "wick/types"),
659 ConfigurationKind::Tests => write!(f, "wick/tests"),
660 ConfigurationKind::Lockdown => write!(f, "wick/lockdown"),
661 }
662 }
663}