scientific_workflow/configuration/parameters/
resolved_configuration.rs1use std::fmt;
4use std::iter::FusedIterator;
5use std::sync::{Arc, OnceLock};
6
7use serde::de::DeserializeOwned;
8use serde_json::Value;
9
10use super::super::error::ConfigurationError;
11use super::super::parameter_key_tuple::ParameterKeyTuple;
12use super::super::parameter_path::ParameterPath;
13use super::super::parameter_tree::{lookup_path, reconstruct};
14use super::WorkloadConfigurationInner;
15use super::reconstruction;
16
17pub struct ResolvedConfiguration {
19 inner: Arc<WorkloadConfigurationInner>,
20 ordinal: u64,
21 resolved: OnceLock<Value>,
22}
23
24impl Clone for ResolvedConfiguration {
25 fn clone(&self) -> Self {
26 Self {
27 inner: Arc::clone(&self.inner),
28 ordinal: self.ordinal,
29 resolved: OnceLock::new(),
30 }
31 }
32}
33
34impl ResolvedConfiguration {
35 pub(super) fn new(inner: Arc<WorkloadConfigurationInner>, ordinal: u64) -> Self {
36 Self {
37 inner,
38 ordinal,
39 resolved: OnceLock::new(),
40 }
41 }
42
43 pub fn ordinal(&self) -> u64 {
45 self.ordinal
46 }
47
48 pub fn component(&self) -> &str {
50 &self.inner.component
51 }
52
53 pub fn workload(&self) -> &str {
55 &self.inner.workload
56 }
57
58 pub fn global_ordinal(&self) -> u64 {
60 self.inner.scope_ordinal(self.ordinal, 0)
61 }
62
63 pub fn component_ordinal(&self) -> u64 {
65 self.inner.scope_ordinal(self.ordinal, 1)
66 }
67
68 pub fn workload_ordinal(&self) -> u64 {
70 self.inner.scope_ordinal(self.ordinal, 2)
71 }
72
73 pub fn value(&self, key: &str) -> Option<&Value> {
75 let path = ParameterPath::parse(key)?;
76 if let Some(leaf) = self.inner.fixed_leaf(&path) {
77 return Some(&leaf.value);
78 }
79 if let Some(leaf) = self
80 .inner
81 .selected_leaves(self.ordinal)
82 .find(|leaf| leaf.path == path)
83 {
84 return Some(&leaf.value);
85 }
86 lookup_path(self.resolved_document(), &path)
87 }
88
89 pub fn require_value(&self, key: &str) -> Result<&Value, ConfigurationError> {
91 self.value(key)
92 .ok_or_else(|| ConfigurationError::UnknownConfigurationValue {
93 ordinal: self.ordinal,
94 key: key.to_owned(),
95 })
96 }
97
98 pub fn decode_value<T>(&self, key: &str) -> Result<T, ConfigurationError>
100 where
101 T: DeserializeOwned,
102 {
103 let Some(path) = ParameterPath::parse(key) else {
104 return Err(ConfigurationError::UnknownConfigurationValue {
105 ordinal: self.ordinal,
106 key: key.to_owned(),
107 });
108 };
109 if let Some(value) = self.exact_leaf(&path) {
110 return T::deserialize(value).map_err(|source| {
111 ConfigurationError::DecodeConfigurationValue {
112 ordinal: self.ordinal,
113 key: key.to_owned(),
114 source,
115 }
116 });
117 }
118 let subtree = reconstruct(
119 self.inner
120 .fixed_leaves()
121 .chain(self.inner.selected_leaves(self.ordinal))
122 .filter(|leaf| path.is_ancestor_of(&leaf.path)),
123 );
124 let Some(value) = lookup_path(&subtree, &path) else {
125 return Err(ConfigurationError::UnknownConfigurationValue {
126 ordinal: self.ordinal,
127 key: key.to_owned(),
128 });
129 };
130 T::deserialize(value).map_err(|source| ConfigurationError::DecodeConfigurationValue {
131 ordinal: self.ordinal,
132 key: key.to_owned(),
133 source,
134 })
135 }
136
137 pub fn decode_values<Values, Keys>(&self, keys: Keys) -> Result<Values, ConfigurationError>
139 where
140 Keys: ParameterKeyTuple<Values>,
141 {
142 keys.decode(self)
143 }
144
145 pub fn contains(&self, key: &str) -> bool {
147 let Some(path) = ParameterPath::parse(key) else {
148 return false;
149 };
150 self.inner
151 .fixed_leaves()
152 .any(|leaf| leaf.path == path || path.is_ancestor_of(&leaf.path))
153 || self
154 .inner
155 .selected_leaves(self.ordinal)
156 .any(|leaf| leaf.path == path || path.is_ancestor_of(&leaf.path))
157 }
158
159 pub fn len(&self) -> usize {
161 self.inner.fixed_leaves().count() + self.inner.selected_leaves(self.ordinal).count()
162 }
163
164 pub fn is_empty(&self) -> bool {
166 self.len() == 0
167 }
168
169 pub fn keys(&self) -> impl Iterator<Item = &str> {
171 self.inner
172 .fixed_leaves()
173 .map(|leaf| leaf.path.identifier())
174 .chain(
175 self.inner
176 .selected_leaves(self.ordinal)
177 .map(|leaf| leaf.path.identifier()),
178 )
179 }
180
181 pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
183 self.inner
184 .fixed_leaves()
185 .map(|leaf| (leaf.path.identifier(), &leaf.value))
186 .chain(
187 self.inner
188 .selected_leaves(self.ordinal)
189 .map(|leaf| (leaf.path.identifier(), &leaf.value)),
190 )
191 }
192
193 pub fn to_json(&self) -> String {
195 self.resolved_document().to_string()
196 }
197
198 pub fn to_json_value(&self) -> Value {
203 self.resolved_document().clone()
204 }
205
206 fn resolved_document(&self) -> &Value {
207 self.resolved
208 .get_or_init(|| reconstruction::document(&self.inner, self.ordinal))
209 }
210
211 pub(crate) fn resolved_object(&self) -> &serde_json::Map<String, Value> {
212 self.resolved_document()
213 .as_object()
214 .expect("resolved configurations always form a JSON object")
215 }
216
217 fn exact_leaf(&self, path: &ParameterPath) -> Option<&Value> {
218 if let Some(leaf) = self.inner.fixed_leaf(path) {
219 return Some(&leaf.value);
220 }
221 self.inner
222 .selected_leaves(self.ordinal)
223 .find(|leaf| &leaf.path == path)
224 .map(|leaf| &leaf.value)
225 }
226}
227
228impl fmt::Debug for ResolvedConfiguration {
229 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
230 formatter
231 .debug_struct("ResolvedConfiguration")
232 .field("component", &self.component())
233 .field("workload", &self.workload())
234 .field("ordinal", &self.ordinal)
235 .field("values", &self.len())
236 .finish_non_exhaustive()
237 }
238}
239
240#[derive(Clone)]
242pub struct ConfigurationIter {
243 inner: Arc<WorkloadConfigurationInner>,
244 next: u64,
245 end: u64,
246}
247
248impl ConfigurationIter {
249 pub(super) fn new(inner: Arc<WorkloadConfigurationInner>, end: u64) -> Self {
250 Self {
251 inner,
252 next: 0,
253 end,
254 }
255 }
256}
257
258impl Iterator for ConfigurationIter {
259 type Item = ResolvedConfiguration;
260
261 fn next(&mut self) -> Option<Self::Item> {
262 if self.next == self.end {
263 return None;
264 }
265 let ordinal = self.next;
266 self.next += 1;
267 Some(ResolvedConfiguration::new(Arc::clone(&self.inner), ordinal))
268 }
269
270 fn size_hint(&self) -> (usize, Option<usize>) {
271 let remaining = self.end - self.next;
272 match usize::try_from(remaining) {
273 Ok(remaining) => (remaining, Some(remaining)),
274 Err(_) => (usize::MAX, None),
275 }
276 }
277}
278
279impl FusedIterator for ConfigurationIter {}
280
281impl fmt::Debug for ConfigurationIter {
282 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
283 formatter
284 .debug_struct("ConfigurationIter")
285 .field("next", &self.next)
286 .field("end", &self.end)
287 .finish_non_exhaustive()
288 }
289}