diff --git a/crates/jrsonnet-evaluator/src/obj.rs b/crates/jrsonnet-evaluator/src/obj.rs index e00b8d2f..106359e8 100644 --- a/crates/jrsonnet-evaluator/src/obj.rs +++ b/crates/jrsonnet-evaluator/src/obj.rs @@ -957,9 +957,9 @@ impl ObjMemberBuilder> { /// Tries to insert value, returns an error if it was already defined pub fn try_value(self, value: impl Into) -> Result<()> { - self.thunk(Thunk::evaluated(value.into())) + self.try_thunk(Thunk::evaluated(value.into())) } - pub fn thunk(self, value: impl Into>) -> Result<()> { + pub fn try_thunk(self, value: impl Into>) -> Result<()> { self.binding(MaybeUnbound::Bound(value.into())) } pub fn bindable(self, bindable: impl Unbound) -> Result<()> { diff --git a/crates/jrsonnet-macros/src/lib.rs b/crates/jrsonnet-macros/src/lib.rs index c154e7fa..20a22f19 100644 --- a/crates/jrsonnet-macros/src/lib.rs +++ b/crates/jrsonnet-macros/src/lib.rs @@ -477,6 +477,7 @@ struct TypedField { ident: Ident, ty: Type, is_option: bool, + is_lazy: bool, } impl TypedField { fn parse(field: &syn::Field) -> Result { @@ -503,11 +504,14 @@ impl TypedField { )); } + let is_lazy = type_is_path(&ty, "Thunk").is_some(); + Ok(Self { attr, ident, ty, is_option, + is_lazy, }) } /// None if this field is flattened in jsonnet output @@ -596,21 +600,33 @@ impl TypedField { } else { quote! {} }; - if self.is_option { + let value = if self.is_lazy { quote! { - if let Some(value) = self.#ident { - out.field(#name) - #hide - #add - .try_value(<#ty as Typed>::into_untyped(value)?)?; - } + out.field(#name) + #hide + #add + .try_thunk(<#ty as Typed>::into_lazy_untyped(value))?; } } else { quote! { out.field(#name) #hide #add - .try_value(<#ty as Typed>::into_untyped(self.#ident)?)?; + .try_value(<#ty as Typed>::into_untyped(value)?)?; + } + }; + if self.is_option { + quote! { + if let Some(value) = self.#ident { + #value + } + } + } else { + quote! { + { + let value = self.#ident; + #value + } } } },