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