difftreelog
feat unify Arg and Typed handling for Thunk
in: master
8 files changed
crates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/ctx.rs
+++ b/crates/jrsonnet-evaluator/src/ctx.rs
@@ -81,7 +81,7 @@
#[must_use]
pub fn into_future(self, ctx: Pending<Self>) -> Self {
{
- ctx.0.borrow_mut().replace(self);
+ ctx.clone().fill(self);
}
ctx.unwrap()
}
crates/jrsonnet-evaluator/src/dynamic.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/dynamic.rs
+++ b/crates/jrsonnet-evaluator/src/dynamic.rs
@@ -1,29 +1,49 @@
-use std::cell::RefCell;
+use std::cell::OnceCell;
use jrsonnet_gcmodule::{Cc, Trace};
+use crate::{error::ErrorKind::InfiniteRecursionDetected, throw, val::ThunkValue, Result, Thunk};
+
// TODO: Replace with OnceCell once in std
#[derive(Clone, Trace)]
-pub struct Pending<V: Trace + 'static>(pub Cc<RefCell<Option<V>>>);
+pub struct Pending<V: Trace + 'static>(pub Cc<OnceCell<V>>);
impl<T: Trace + 'static> Pending<T> {
pub fn new() -> Self {
- Self(Cc::new(RefCell::new(None)))
+ Self(Cc::new(OnceCell::new()))
}
pub fn new_filled(v: T) -> Self {
- Self(Cc::new(RefCell::new(Some(v))))
+ let cell = OnceCell::new();
+ let _ = cell.set(v);
+ Self(Cc::new(cell))
}
/// # Panics
/// If wrapper is filled already
pub fn fill(self, value: T) {
- assert!(self.0.borrow().is_none(), "wrapper is filled already");
- self.0.borrow_mut().replace(value);
+ self.0
+ .set(value)
+ .map_err(|_| ())
+ .expect("wrapper is filled already")
}
}
impl<T: Clone + Trace + 'static> Pending<T> {
/// # Panics
/// If wrapper is not yet filled
pub fn unwrap(&self) -> T {
- self.0.borrow().as_ref().cloned().unwrap()
+ self.0.get().cloned().expect("pending was not filled")
+ }
+ pub fn try_get(&self) -> Option<T> {
+ self.0.get().cloned()
+ }
+}
+
+impl<T: Trace + Clone> ThunkValue for Pending<T> {
+ type Output = T;
+
+ fn get(self: Box<Self>) -> Result<Self::Output> {
+ let Some(value) = self.0.get() else {
+ throw!(InfiniteRecursionDetected);
+ };
+ Ok(value.clone())
}
}
@@ -32,3 +52,9 @@
Self::new()
}
}
+
+impl<T: Trace + Clone> Into<Thunk<T>> for Pending<T> {
+ fn into(self) -> Thunk<T> {
+ Thunk::new(self)
+ }
+}
crates/jrsonnet-evaluator/src/function/arglike.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/arglike.rs
+++ b/crates/jrsonnet-evaluator/src/function/arglike.rs
@@ -48,28 +48,22 @@
where
T: Typed + Clone,
{
- fn evaluate_arg(&self, _ctx: Context, _tailstrict: bool) -> Result<Thunk<Val>> {
+ fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {
+ if T::provides_lazy() && !tailstrict {
+ return Ok(T::into_lazy_untyped(self.clone()));
+ }
let val = T::into_untyped(self.clone())?;
Ok(Thunk::evaluated(val))
}
}
impl<T> OptionalContext for T where T: Typed + Clone {}
-impl ArgLike for Thunk<Val> {
- fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {
- if tailstrict {
- self.force()?;
- }
- Ok(self.clone())
- }
-}
-impl OptionalContext for Thunk<Val> {}
-
#[derive(Clone, Trace)]
pub enum TlaArg {
String(IStr),
Code(LocExpr),
Val(Val),
+ Lazy(Thunk<Val>),
}
impl ArgLike for TlaArg {
fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {
@@ -84,6 +78,7 @@
})
}),
TlaArg::Val(val) => Ok(Thunk::evaluated(val.clone())),
+ TlaArg::Lazy(lazy) => Ok(lazy.clone()),
}
}
}
crates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/obj.rs
+++ b/crates/jrsonnet-evaluator/src/obj.rs
@@ -15,7 +15,9 @@
function::CallLocation,
gc::{GcHashMap, GcHashSet, TraceBox},
operator::evaluate_add_op,
- tb, throw, MaybeUnbound, Result, State, Thunk, Unbound, Val,
+ tb, throw,
+ val::ThunkValue,
+ MaybeUnbound, Result, State, Thunk, Unbound, Val,
};
#[cfg(not(feature = "exp-preserve-order"))]
crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth1use std::{collections::BTreeMap, marker::PhantomData, ops::Deref};23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::{IBytes, IStr};5pub use jrsonnet_macros::Typed;6use jrsonnet_types::{ComplexValType, ValType};78use crate::{9 arr::ArrValue,10 error::Result,11 function::{native::NativeDesc, FuncDesc, FuncVal},12 throw,13 typed::CheckType,14 val::{IndexableVal, StrValue, ThunkMapper},15 ObjValue, ObjValueBuilder, Thunk, Val,16};1718#[derive(Trace)]19struct FromUntyped<K: Trace>(PhantomData<fn() -> K>);20impl<K> ThunkMapper<Val> for FromUntyped<K>21where22 K: Typed + Trace,23{24 type Output = K;2526 fn map(self, from: Val) -> Result<Self::Output> {27 K::from_untyped(from)28 }29}30impl<K: Trace> Default for FromUntyped<K> {31 fn default() -> Self {32 Self(PhantomData)33 }34}3536pub trait TypedObj: Typed {37 fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;38 fn parse(obj: &ObjValue) -> Result<Self>;39 fn into_object(self) -> Result<ObjValue> {40 let mut builder = ObjValueBuilder::new();41 self.serialize(&mut builder)?;42 Ok(builder.build())43 }44}4546pub trait Typed: Sized {47 const TYPE: &'static ComplexValType;48 fn into_untyped(typed: Self) -> Result<Val>;49 fn into_lazy_untyped(typed: Self) -> Thunk<Val> {50 Thunk::from(Self::into_untyped(typed))51 }52 fn from_untyped(untyped: Val) -> Result<Self>;53 fn from_lazy_untyped(lazy: Thunk<Val>) -> Result<Self> {54 Self::from_untyped(lazy.evaluate()?)55 }5657 // Whatever caller should use `into_lazy_untyped` instead of `into_untyped`58 fn provides_lazy() -> bool {59 false60 }6162 // Whatever caller should use `from_lazy_untyped` instead of `from_untyped` when possible63 fn wants_lazy() -> bool {64 false65 }6667 /// Hack to make builtins be able to return non-result values, and make macros able to convert those values to result68 /// This method returns identity in impl Typed for Result, and should not be overriden69 #[doc(hidden)]70 fn into_result(typed: Self) -> Result<Val> {71 let value = Self::into_untyped(typed)?;72 Ok(value)73 }74}7576impl<T> Typed for Thunk<T>77where78 T: Typed + Trace + Clone,79{80 const TYPE: &'static ComplexValType = &ComplexValType::Lazy(T::TYPE);8182 fn into_untyped(typed: Self) -> Result<Val> {83 T::into_untyped(typed.evaluate()?)84 }8586 fn from_untyped(untyped: Val) -> Result<Self> {87 Self::from_lazy_untyped(Thunk::evaluated(untyped))88 }8990 fn provides_lazy() -> bool {91 true92 }9394 fn into_lazy_untyped(inner: Self) -> Thunk<Val> {95 #[derive(Trace)]96 struct IntoUntyped<K: Trace>(PhantomData<fn() -> K>);97 impl<K> ThunkMapper<K> for IntoUntyped<K>98 where99 K: Typed + Trace,100 {101 type Output = Val;102103 fn map(self, from: K) -> Result<Self::Output> {104 K::into_untyped(from)105 }106 }107 impl<K: Trace> Default for IntoUntyped<K> {108 fn default() -> Self {109 Self(PhantomData)110 }111 }112 inner.map(<IntoUntyped<T>>::default())113 }114115 fn wants_lazy() -> bool {116 true117 }118119 fn from_lazy_untyped(inner: Thunk<Val>) -> Result<Self> {120 Ok(inner.map(<FromUntyped<T>>::default()))121 }122}123124const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS + 1)) - 1) as f64;125126macro_rules! impl_int {127 ($($ty:ty)*) => {$(128 impl Typed for $ty {129 const TYPE: &'static ComplexValType =130 &ComplexValType::BoundedNumber(Some(Self::MIN as f64), Some(Self::MAX as f64));131 fn from_untyped(value: Val) -> Result<Self> {132 <Self as Typed>::TYPE.check(&value)?;133 match value {134 Val::Num(n) => {135 #[allow(clippy::float_cmp)]136 if n.trunc() != n {137 throw!(138 "cannot convert number with fractional part to {}",139 stringify!($ty)140 )141 }142 Ok(n as Self)143 }144 _ => unreachable!(),145 }146 }147 fn into_untyped(value: Self) -> Result<Val> {148 Ok(Val::Num(value as f64))149 }150 }151 )*};152}153154impl_int!(i8 u8 i16 u16 i32 u32);155156macro_rules! impl_bounded_int {157 ($($name:ident = $ty:ty)*) => {$(158 #[derive(Clone, Copy)]159 pub struct $name<const MIN: $ty, const MAX: $ty>($ty);160 impl<const MIN: $ty, const MAX: $ty> $name<MIN, MAX> {161 pub const fn new(value: $ty) -> Option<$name<MIN, MAX>> {162 if value >= MIN && value <= MAX {163 Some(Self(value))164 } else {165 None166 }167 }168 pub const fn value(self) -> $ty {169 self.0170 }171 }172 impl<const MIN: $ty, const MAX: $ty> Deref for $name<MIN, MAX> {173 type Target = $ty;174 fn deref(&self) -> &Self::Target {175 &self.0176 }177 }178179 impl<const MIN: $ty, const MAX: $ty> Typed for $name<MIN, MAX> {180 const TYPE: &'static ComplexValType =181 &ComplexValType::BoundedNumber(182 Some(MIN as f64),183 Some(MAX as f64),184 );185186 fn from_untyped(value: Val) -> Result<Self> {187 <Self as Typed>::TYPE.check(&value)?;188 match value {189 Val::Num(n) => {190 #[allow(clippy::float_cmp)]191 if n.trunc() != n {192 throw!(193 "cannot convert number with fractional part to {}",194 stringify!($ty)195 )196 }197 Ok(Self(n as $ty))198 }199 _ => unreachable!(),200 }201 }202203 fn into_untyped(value: Self) -> Result<Val> {204 Ok(Val::Num(value.0 as f64))205 }206 }207 )*};208}209210impl_bounded_int!(211 BoundedI8 = i8212 BoundedI16 = i16213 BoundedI32 = i32214 BoundedI64 = i64215 BoundedUsize = usize216);217218impl Typed for f64 {219 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);220221 fn into_untyped(value: Self) -> Result<Val> {222 Ok(Val::Num(value))223 }224225 fn from_untyped(value: Val) -> Result<Self> {226 <Self as Typed>::TYPE.check(&value)?;227 match value {228 Val::Num(n) => Ok(n),229 _ => unreachable!(),230 }231 }232}233234pub struct PositiveF64(pub f64);235impl Typed for PositiveF64 {236 const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(0.0), None);237238 fn into_untyped(value: Self) -> Result<Val> {239 Ok(Val::Num(value.0))240 }241242 fn from_untyped(value: Val) -> Result<Self> {243 <Self as Typed>::TYPE.check(&value)?;244 match value {245 Val::Num(n) => Ok(Self(n)),246 _ => unreachable!(),247 }248 }249}250impl Typed for usize {251 const TYPE: &'static ComplexValType =252 &ComplexValType::BoundedNumber(Some(0.0), Some(MAX_SAFE_INTEGER));253254 fn into_untyped(value: Self) -> Result<Val> {255 if value > MAX_SAFE_INTEGER as Self {256 throw!("number is too large")257 }258 Ok(Val::Num(value as f64))259 }260261 fn from_untyped(value: Val) -> Result<Self> {262 <Self as Typed>::TYPE.check(&value)?;263 match value {264 Val::Num(n) => {265 #[allow(clippy::float_cmp)]266 if n.trunc() != n {267 throw!("cannot convert number with fractional part to usize")268 }269 Ok(n as Self)270 }271 _ => unreachable!(),272 }273 }274}275276impl Typed for IStr {277 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);278279 fn into_untyped(value: Self) -> Result<Val> {280 Ok(Val::Str(StrValue::Flat(value)))281 }282283 fn from_untyped(value: Val) -> Result<Self> {284 <Self as Typed>::TYPE.check(&value)?;285 match value {286 Val::Str(s) => Ok(s.into_flat()),287 _ => unreachable!(),288 }289 }290}291292impl Typed for String {293 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);294295 fn into_untyped(value: Self) -> Result<Val> {296 Ok(Val::Str(StrValue::Flat(value.into())))297 }298299 fn from_untyped(value: Val) -> Result<Self> {300 <Self as Typed>::TYPE.check(&value)?;301 match value {302 Val::Str(s) => Ok(s.to_string()),303 _ => unreachable!(),304 }305 }306}307308impl Typed for char {309 const TYPE: &'static ComplexValType = &ComplexValType::Char;310311 fn into_untyped(value: Self) -> Result<Val> {312 Ok(Val::Str(StrValue::Flat(value.to_string().into())))313 }314315 fn from_untyped(value: Val) -> Result<Self> {316 <Self as Typed>::TYPE.check(&value)?;317 match value {318 Val::Str(s) => Ok(s.into_flat().chars().next().unwrap()),319 _ => unreachable!(),320 }321 }322}323324impl<T> Typed for Vec<T>325where326 T: Typed,327{328 const TYPE: &'static ComplexValType = &ComplexValType::ArrayRef(T::TYPE);329330 fn into_untyped(value: Self) -> Result<Val> {331 Ok(Val::Arr(332 value333 .into_iter()334 .map(T::into_untyped)335 .collect::<Result<ArrValue>>()?,336 ))337 }338339 fn from_untyped(value: Val) -> Result<Self> {340 let Val::Arr(a) = value else {341 <Self as Typed>::TYPE.check(&value)?;342 unreachable!("typecheck should fail")343 };344 a.iter()345 .map(|r| r.and_then(T::from_untyped))346 .collect::<Result<Vec<T>>>()347 }348}349350impl Typed for Val {351 const TYPE: &'static ComplexValType = &ComplexValType::Any;352353 fn into_untyped(typed: Self) -> Result<Val> {354 Ok(typed)355 }356 fn from_untyped(untyped: Val) -> Result<Self> {357 Ok(untyped)358 }359}360361// Hack362#[doc(hidden)]363impl<T> Typed for Result<T>364where365 T: Typed,366{367 const TYPE: &'static ComplexValType = &ComplexValType::Any;368369 fn into_untyped(_typed: Self) -> Result<Val> {370 panic!("do not use this conversion")371 }372373 fn from_untyped(_untyped: Val) -> Result<Self> {374 panic!("do not use this conversion")375 }376377 fn into_result(typed: Self) -> Result<Val> {378 typed.map(T::into_untyped)?379 }380}381382/// Specialization383impl Typed for IBytes {384 const TYPE: &'static ComplexValType =385 &ComplexValType::ArrayRef(&ComplexValType::BoundedNumber(Some(0.0), Some(255.0)));386387 fn into_untyped(value: Self) -> Result<Val> {388 Ok(Val::Arr(ArrValue::bytes(value)))389 }390391 fn from_untyped(value: Val) -> Result<Self> {392 if let Val::Arr(ArrValue::Bytes(bytes)) = value {393 return Ok(bytes.0);394 }395 <Self as Typed>::TYPE.check(&value)?;396 match value {397 Val::Arr(a) => {398 let mut out = Vec::with_capacity(a.len());399 for e in a.iter() {400 let r = e?;401 out.push(u8::from_untyped(r)?);402 }403 Ok(out.as_slice().into())404 }405 _ => unreachable!(),406 }407 }408}409410pub struct M1;411impl Typed for M1 {412 const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(-1.0), Some(-1.0));413414 fn into_untyped(_: Self) -> Result<Val> {415 Ok(Val::Num(-1.0))416 }417418 fn from_untyped(value: Val) -> Result<Self> {419 <Self as Typed>::TYPE.check(&value)?;420 Ok(Self)421 }422}423424macro_rules! decl_either {425 ($($name: ident, $($id: ident)*);*) => {$(426 #[derive(Clone)]427 pub enum $name<$($id),*> {428 $($id($id)),*429 }430 impl<$($id),*> Typed for $name<$($id),*>431 where432 $($id: Typed,)*433 {434 const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[$($id::TYPE),*]);435436 fn into_untyped(value: Self) -> Result<Val> {437 match value {$(438 $name::$id(v) => $id::into_untyped(v)439 ),*}440 }441442 fn from_untyped(value: Val) -> Result<Self> {443 $(444 if $id::TYPE.check(&value).is_ok() {445 $id::from_untyped(value).map(Self::$id)446 } else447 )* {448 <Self as Typed>::TYPE.check(&value)?;449 unreachable!()450 }451 }452 }453 )*}454}455decl_either!(456 Either1, A;457 Either2, A B;458 Either3, A B C;459 Either4, A B C D;460 Either5, A B C D E;461 Either6, A B C D E F;462 Either7, A B C D E F G463);464#[macro_export]465macro_rules! Either {466 ($a:ty) => {Either1<$a>};467 ($a:ty, $b:ty) => {Either2<$a, $b>};468 ($a:ty, $b:ty, $c:ty) => {Either3<$a, $b, $c>};469 ($a:ty, $b:ty, $c:ty, $d:ty) => {Either4<$a, $b, $c, $d>};470 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty) => {Either5<$a, $b, $c, $d, $e>};471 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty) => {Either6<$a, $b, $c, $d, $e, $f>};472 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty, $g:ty) => {Either7<$a, $b, $c, $d, $e, $f, $g>};473}474pub use Either;475476pub type MyType = Either![u32, f64, String];477478impl Typed for ArrValue {479 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);480481 fn into_untyped(value: Self) -> Result<Val> {482 Ok(Val::Arr(value))483 }484485 fn from_untyped(value: Val) -> Result<Self> {486 <Self as Typed>::TYPE.check(&value)?;487 match value {488 Val::Arr(a) => Ok(a),489 _ => unreachable!(),490 }491 }492}493494impl Typed for FuncVal {495 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);496497 fn into_untyped(value: Self) -> Result<Val> {498 Ok(Val::Func(value))499 }500501 fn from_untyped(value: Val) -> Result<Self> {502 <Self as Typed>::TYPE.check(&value)?;503 match value {504 Val::Func(a) => Ok(a),505 _ => unreachable!(),506 }507 }508}509510impl Typed for Cc<FuncDesc> {511 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);512513 fn into_untyped(value: Self) -> Result<Val> {514 Ok(Val::Func(FuncVal::Normal(value)))515 }516517 fn from_untyped(value: Val) -> Result<Self> {518 <Self as Typed>::TYPE.check(&value)?;519 match value {520 Val::Func(FuncVal::Normal(desc)) => Ok(desc),521 Val::Func(_) => throw!("expected normal function, not builtin"),522 _ => unreachable!(),523 }524 }525}526527impl Typed for ObjValue {528 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Obj);529530 fn into_untyped(value: Self) -> Result<Val> {531 Ok(Val::Obj(value))532 }533534 fn from_untyped(value: Val) -> Result<Self> {535 <Self as Typed>::TYPE.check(&value)?;536 match value {537 Val::Obj(a) => Ok(a),538 _ => unreachable!(),539 }540 }541}542543impl Typed for bool {544 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Bool);545546 fn into_untyped(value: Self) -> Result<Val> {547 Ok(Val::Bool(value))548 }549550 fn from_untyped(value: Val) -> Result<Self> {551 <Self as Typed>::TYPE.check(&value)?;552 match value {553 Val::Bool(a) => Ok(a),554 _ => unreachable!(),555 }556 }557}558impl Typed for IndexableVal {559 const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[560 &ComplexValType::Simple(ValType::Arr),561 &ComplexValType::Simple(ValType::Str),562 ]);563564 fn into_untyped(value: Self) -> Result<Val> {565 match value {566 IndexableVal::Str(s) => Ok(Val::Str(StrValue::Flat(s))),567 IndexableVal::Arr(a) => Ok(Val::Arr(a)),568 }569 }570571 fn from_untyped(value: Val) -> Result<Self> {572 <Self as Typed>::TYPE.check(&value)?;573 value.into_indexable()574 }575}576577pub struct Null;578impl Typed for Null {579 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Null);580581 fn into_untyped(_: Self) -> Result<Val> {582 Ok(Val::Null)583 }584585 fn from_untyped(value: Val) -> Result<Self> {586 <Self as Typed>::TYPE.check(&value)?;587 Ok(Self)588 }589}590591pub struct NativeFn<D: NativeDesc>(D::Value);592impl<D: NativeDesc> Deref for NativeFn<D> {593 type Target = D::Value;594595 fn deref(&self) -> &Self::Target {596 &self.0597 }598}599impl<D: NativeDesc> Typed for NativeFn<D> {600 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);601602 fn into_untyped(_typed: Self) -> Result<Val> {603 throw!("can only convert functions from jsonnet to native")604 }605606 fn from_untyped(untyped: Val) -> Result<Self> {607 Ok(Self(608 untyped609 .as_func()610 .expect("shape is checked")611 .into_native::<D>(),612 ))613 }614}crates/jrsonnet-evaluator/src/typed/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/mod.rs
+++ b/crates/jrsonnet-evaluator/src/typed/mod.rs
@@ -252,6 +252,7 @@
}
Ok(())
}
+ Self::Lazy(_lazy) => Ok(()),
}
}
}
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -88,6 +88,54 @@
}
}
+pub trait ThunkMapper<Input>: Trace {
+ type Output;
+ fn map(self, from: Input) -> Result<Self::Output>;
+}
+impl<Input> Thunk<Input>
+where
+ Input: Trace + Clone,
+{
+ pub fn map<M>(self, mapper: M) -> Thunk<M::Output>
+ where
+ M: ThunkMapper<Input>,
+ M::Output: Trace,
+ {
+ #[derive(Trace)]
+ struct Mapped<Input: Trace, Mapper: Trace> {
+ inner: Thunk<Input>,
+ mapper: Mapper,
+ }
+ impl<Input, Mapper> ThunkValue for Mapped<Input, Mapper>
+ where
+ Input: Trace + Clone,
+ Mapper: ThunkMapper<Input>,
+ {
+ type Output = Mapper::Output;
+
+ fn get(self: Box<Self>) -> Result<Self::Output> {
+ let value = self.inner.evaluate()?;
+ let mapped = self.mapper.map(value)?;
+ Ok(mapped)
+ }
+ }
+
+ Thunk::new(Mapped::<Input, M> {
+ inner: self,
+ mapper,
+ })
+ }
+}
+
+impl<T: Trace> From<Result<T>> for Thunk<T> {
+ fn from(value: Result<T>) -> Self {
+ match value {
+ Ok(o) => Self::evaluated(o),
+ Err(e) => Self::errored(e),
+ }
+ }
+}
+
type CacheKey = (Option<WeakObjValue>, Option<WeakObjValue>);
#[derive(Trace, Clone)]
@@ -272,6 +320,11 @@
Self::Flat(value.into())
}
}
+impl From<IStr> for StrValue {
+ fn from(value: IStr) -> Self {
+ Self::Flat(value)
+ }
+}
impl Display for StrValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
crates/jrsonnet-types/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-types/src/lib.rs
+++ b/crates/jrsonnet-types/src/lib.rs
@@ -128,10 +128,12 @@
Array(Box<ComplexValType>),
ArrayRef(&'static ComplexValType),
ObjectRef(&'static [(&'static str, &'static ComplexValType)]),
+ AttrsOf(&'static ComplexValType),
Union(Vec<ComplexValType>),
UnionRef(&'static [&'static ComplexValType]),
Sum(Vec<ComplexValType>),
SumRef(&'static [&'static ComplexValType]),
+ Lazy(&'static ComplexValType),
}
impl From<ValType> for ComplexValType {
@@ -195,10 +197,18 @@
}
write!(f, "}}")?;
}
+ ComplexValType::AttrsOf(a) => {
+ if matches!(a, ComplexValType::Any) {
+ write!(f, "object")?;
+ } else {
+ write!(f, "AttrsOf<{a}>")?;
+ }
+ }
ComplexValType::Union(v) => write_union(f, true, v.iter())?,
ComplexValType::UnionRef(v) => write_union(f, true, v.iter().copied())?,
ComplexValType::Sum(v) => write_union(f, false, v.iter())?,
ComplexValType::SumRef(v) => write_union(f, false, v.iter().copied())?,
+ ComplexValType::Lazy(lazy) => write!(f, "Lazy<{lazy}>")?,
};
Ok(())
}