Skip to content

Commit 6f75bd1

Browse files
committed
Very simplistic draft of reference default values
We treat `::` as the delimiter between reference path and default value. The implementation currently first resolves all nested references in both the reference and the default value parts before even looking for `:` and `::`. Right now, the default value is always emitted as a string. The current plan is to simply try to parse the default value as JSON to allow complex default values (directly or via reference). We still need to decide how exactly we want users to provide literal defaults (possibly as '${ref:path::`<valid JSON>`}' so that literal booleans etc. work correctly.
1 parent e8c9d9b commit 6f75bd1

1 file changed

Lines changed: 46 additions & 16 deletions

File tree

src/refs/mod.rs

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,14 @@ impl Token {
207207
}
208208
// Construct flattened ref path by resolving any potential nested references in the
209209
// Ref's Vec<Token>.
210-
let path = interpolate_token_slice(parts, params, state, opts)?;
210+
let path_with_default = interpolate_token_slice(parts, params, state, opts)?;
211+
212+
let (path, default) =
213+
if let Some((path, default)) = path_with_default.split_once("::") {
214+
(path.to_string(), Some(default))
215+
} else {
216+
(path_with_default, None)
217+
};
211218

212219
if state.seen_paths.contains(&path) {
213220
// we've already seen this reference, so we know there's a loop, and can abort
@@ -223,9 +230,17 @@ impl Token {
223230
let k0 = refpath_iter.next().unwrap();
224231
// v is the value which we update to point to the next value as we recursively
225232
// descend into the params Mapping
226-
let mut v = params
227-
.get(&k0.into())
228-
.ok_or_else(|| state.render_missing_key_error(&path, k0))?;
233+
let v = params.get(&k0.into());
234+
let mut v = if v.is_none()
235+
&& let Some(dv) = default
236+
{
237+
// TODO(sg): parse dv as json
238+
// TODO(sg): make sure we return the default value as a Literal if
239+
// it's a string to avoid incorrect re-resolution.
240+
return Ok(Value::from(dv));
241+
} else {
242+
v.ok_or_else(|| state.render_missing_key_error(&path, k0))?
243+
};
229244

230245
// newv is used to hold temporary Values generated by interpolating v
231246
let mut newv;
@@ -249,9 +264,17 @@ impl Token {
249264
// trivial case: v is a Mapping, we can just lookup the next value based
250265
// on `key`.
251266
Value::Mapping(_) => {
252-
v = newv
253-
.get(&key.into())
254-
.ok_or_else(|| state.render_missing_key_error(&path, key))?;
267+
let nv = newv.get(&key.into());
268+
v = if nv.is_none()
269+
&& let Some(dv) = default
270+
{
271+
// TODO(sg): parse dv as json
272+
// TODO(sg): make sure we return the default value as a Literal if
273+
// it's a string to avoid incorrect re-resolution.
274+
return Ok(Value::from(dv));
275+
} else {
276+
nv.ok_or_else(|| state.render_missing_key_error(&path, key))?
277+
}
255278
}
256279
// Sequence lookups aren't supported by Python Reclass. We may implement
257280
// them in the future.
@@ -267,15 +290,22 @@ impl Token {
267290
),
268291
// A lookup into any other Value variant is an error
269292
_ => {
270-
return Err(state.render_lookup_error(
271-
&path,
272-
key,
273-
&format!(
274-
"Can't continue lookup, {} is a {}",
275-
traversed.join(":"),
276-
newv.variant()
277-
),
278-
));
293+
if let Some(dv) = default {
294+
// TODO(sg): parse dv as json
295+
// TODO(sg): make sure we return the default value as a Literal if
296+
// it's a string to avoid incorrect re-resolution.
297+
return Ok(Value::from(dv));
298+
} else {
299+
return Err(state.render_lookup_error(
300+
&path,
301+
key,
302+
&format!(
303+
"Can't continue lookup, {} is a {}",
304+
traversed.join(":"),
305+
newv.variant()
306+
),
307+
));
308+
}
279309
}
280310
}
281311
// add current segment to list of traversed segments after lookup is completed.

0 commit comments

Comments
 (0)