wick_config/config/
configuration_tree.rs1#![allow(missing_docs)]
2
3use wick_asset_reference::FetchOptions;
4use wick_packet::RuntimeConfig;
5
6use super::{ImportDefinition, UninitializedConfiguration};
7use crate::config::{Binding, ComponentDefinition};
8use crate::error::ManifestError;
9use crate::{Imports, WickConfiguration};
10
11#[derive(Debug)]
12
13pub enum ConfigOrDefinition<T> {
14 Config(ConfigurationTreeNode<T>),
15 Definition { id: String, element: ComponentDefinition },
16}
17
18impl<T> ConfigOrDefinition<T> {
19 #[must_use]
21 #[allow(clippy::missing_const_for_fn)]
22 pub fn as_config(self) -> Option<T> {
23 match self {
24 ConfigOrDefinition::Config(c) => Some(c.element),
25 ConfigOrDefinition::Definition { .. } => None,
26 }
27 }
28 #[must_use]
30 #[allow(clippy::missing_const_for_fn)]
31 pub fn as_component_definition(self) -> Option<(String, ComponentDefinition)> {
32 match self {
33 ConfigOrDefinition::Config(_) => None,
34 ConfigOrDefinition::Definition { id, element } => Some((id, element)),
35 }
36 }
37}
38
39#[derive(Debug)]
40#[non_exhaustive]
41pub struct ConfigurationTreeNode<T> {
42 pub name: String,
43 pub element: T,
44 pub children: Vec<ConfigOrDefinition<T>>,
45}
46
47impl<T> ConfigurationTreeNode<T>
48where
49 T: Imports + Send + Sync,
50{
51 #[must_use]
52 pub const fn new(name: String, element: T, children: Vec<ConfigOrDefinition<T>>) -> Self {
53 Self {
54 name,
55 element,
56 children,
57 }
58 }
59
60 #[must_use]
62 pub fn flatten(self) -> Vec<ConfigOrDefinition<T>> {
63 let id = self.name.clone();
64 flatten(self, &id)
65 }
66}
67
68#[async_recursion::async_recursion]
69pub async fn fetch_children<T, H, U>(
70 config: &T,
71 options: FetchOptions,
72 processor: &H,
73) -> Result<Vec<ConfigOrDefinition<U>>, ManifestError>
74where
75 T: Imports + Send + Sync,
76 H: Fn(Option<RuntimeConfig>, UninitializedConfiguration) -> Result<U, ManifestError> + Send + Sync,
77 U: Imports + Send + Sync,
78{
79 let imports = config.imports().to_vec();
80
81 let mut children = fetch_imports(imports, options.clone(), processor).await?;
82 for child in &mut children {
83 match child {
84 ConfigOrDefinition::Config(ref mut c) => {
85 let children = fetch_children(&c.element, options.clone(), processor).await?;
86 c.children = children;
87 }
88 ConfigOrDefinition::Definition { .. } => {}
89 }
90 }
91 Ok(children)
92}
93
94#[must_use]
95pub fn flatten<T>(node: ConfigurationTreeNode<T>, prefix: &str) -> Vec<ConfigOrDefinition<T>> {
96 let mut flattened = Vec::new();
97 let children = node.children;
98 let new_node = ConfigurationTreeNode {
99 name: prefix.to_owned(),
100 element: node.element,
101 children: Vec::new(),
102 };
103 flattened.push(ConfigOrDefinition::Config(new_node));
104 for child in children {
105 match child {
106 ConfigOrDefinition::Config(c) => {
107 let id = format!("{}::{}", prefix, c.name);
108 let new_node = ConfigurationTreeNode {
109 name: id.clone(),
110 element: c.element,
111 children: c.children,
112 };
113
114 flattened.extend(flatten(new_node, &id));
115 }
116 ConfigOrDefinition::Definition { .. } => flattened.push(child),
117 }
118 }
119 flattened
120}
121
122async fn fetch_imports<H, T>(
123 imports: Vec<Binding<ImportDefinition>>,
124 options: FetchOptions,
125 processor: &H,
126) -> Result<Vec<ConfigOrDefinition<T>>, ManifestError>
127where
128 T: Imports + Send + Sync,
129 H: Fn(Option<RuntimeConfig>, UninitializedConfiguration) -> Result<T, ManifestError> + Send + Sync,
130{
131 let mut children: Vec<ConfigOrDefinition<T>> = Vec::new();
132 for import in imports {
133 let id = import.id().to_owned();
134
135 match import.kind {
136 super::ImportDefinition::Component(c) => match c {
137 super::ComponentDefinition::Manifest(c) => {
138 let config = WickConfiguration::fetch(c.reference.clone(), options.clone()).await?;
139 let config = processor(c.config().and_then(|c| c.value.clone()), config)?;
140
141 children.push(ConfigOrDefinition::Config(ConfigurationTreeNode::new(
142 id,
143 config,
144 Vec::new(),
145 )));
146 }
147 component => {
148 children.push(ConfigOrDefinition::Definition {
149 id,
150 element: component.clone(),
151 });
152 }
153 },
154 super::ImportDefinition::Types(_) => {}
155 }
156 }
157 Ok(children)
158}
159
160#[cfg(test)]
161mod test {
162 use anyhow::Result;
163
164 use super::*;
165 use crate::config::{components, AppConfigurationBuilder, Binding, ComponentDefinition, ImportDefinition};
166
167 #[test_logger::test(tokio::test)]
168 async fn test_tree_walker() -> Result<()> {
169 let mut config = AppConfigurationBuilder::default();
170
171 config
172 .name("app")
173 .options(FetchOptions::default())
174 .import(vec![Binding::new(
175 "SUB_COMPONENT",
176 ImportDefinition::Component(ComponentDefinition::Manifest(
177 components::ManifestComponentBuilder::default()
178 .reference("tests/manifests/v1/component-resources.yaml")
179 .build()?,
180 )),
181 )]);
182 let config = config.build()?;
183 let config = UninitializedConfiguration::new(WickConfiguration::App(config));
184 let children = fetch_children(&config, Default::default(), &|_, c| Ok(c)).await?;
185 assert_eq!(children.len(), 1);
186 Ok(())
187 }
188}