difftreelog
feat add description stacktrace frames for all formats
in: master
5 files changed
crates/jrsonnet-evaluator/src/manifest.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/manifest.rs
+++ b/crates/jrsonnet-evaluator/src/manifest.rs
@@ -183,6 +183,8 @@
cur_padding: &mut String,
options: &JsonFormat<'_>,
) -> Result<()> {
+ use JsonFormatting::*;
+
let mtype = options.mtype;
match val {
Val::Bool(v) => {
@@ -218,89 +220,118 @@
}
Val::Arr(items) => {
buf.push('[');
- if !items.is_empty() {
- if mtype != JsonFormatting::ToString && mtype != JsonFormatting::Minify {
- buf.push_str(options.newline);
+
+ let old_len = cur_padding.len();
+ cur_padding.push_str(&options.padding);
+
+ let mut had_items = false;
+ for (i, item) in items.iter().enumerate() {
+ had_items = true;
+ let item = item.with_description(|| format!("elem <{i}> evaluation"))?;
+
+ if i != 0 {
+ buf.push(',');
}
+ match mtype {
+ Manifest | Std => {
+ buf.push_str(options.newline);
+ buf.push_str(cur_padding);
+ }
+ ToString => buf.push(' '),
+ Minify => {}
+ };
- let old_len = cur_padding.len();
- cur_padding.push_str(&options.padding);
- for (i, item) in items.iter().enumerate() {
- if i != 0 {
- buf.push(',');
- if mtype == JsonFormatting::ToString {
- buf.push(' ');
- } else if mtype != JsonFormatting::Minify {
- buf.push_str(options.newline);
- }
- }
+ buf.push_str(cur_padding);
+ State::push_description(
+ || format!("elem <{i}> manifestification"),
+ || manifest_json_ex_buf(&item, buf, cur_padding, options),
+ )?;
+ }
+
+ cur_padding.truncate(old_len);
+
+ match mtype {
+ Manifest | ToString if !had_items => {
+ // Empty array as "[ ]"
+ buf.push(' ');
+ }
+ Manifest => {
+ buf.push_str(options.newline);
buf.push_str(cur_padding);
- manifest_json_ex_buf(&item?, buf, cur_padding, options)
- .with_description(|| format!("elem <{i}> manifestification"))?;
}
- cur_padding.truncate(old_len);
-
- if mtype != JsonFormatting::ToString && mtype != JsonFormatting::Minify {
+ Std => {
+ if !had_items {
+ // Stdlib formats empty array as "[\n\n]"
+ buf.push_str(options.newline);
+ }
buf.push_str(options.newline);
buf.push_str(cur_padding);
}
- } else if mtype == JsonFormatting::Std {
- buf.push_str(options.newline);
- buf.push_str(options.newline);
- buf.push_str(cur_padding);
- } else if mtype == JsonFormatting::ToString || mtype == JsonFormatting::Manifest {
- buf.push(' ');
+ Minify | ToString => {}
}
+
buf.push(']');
}
Val::Obj(obj) => {
obj.run_assertions()?;
buf.push('{');
- let fields = obj.fields(
- #[cfg(feature = "exp-preserve-order")]
- options.preserve_order,
- );
- if !fields.is_empty() {
- if mtype != JsonFormatting::ToString && mtype != JsonFormatting::Minify {
- buf.push_str(options.newline);
- }
- let old_len = cur_padding.len();
- cur_padding.push_str(&options.padding);
- for (i, field) in fields.into_iter().enumerate() {
- if i != 0 {
- buf.push(',');
- if mtype == JsonFormatting::ToString {
- buf.push(' ');
- } else if mtype != JsonFormatting::Minify {
- buf.push_str(options.newline);
- }
+ let old_len = cur_padding.len();
+ cur_padding.push_str(&options.padding);
+
+ let mut had_fields = false;
+ for (i, (key, value)) in obj
+ .iter(
+ #[cfg(feature = "exp-preserve-order")]
+ options.preserve_order,
+ )
+ .enumerate()
+ {
+ had_fields = true;
+ let value = value.with_description(|| format!("field <{key}> evaluation"))?;
+
+ if i != 0 {
+ buf.push(',');
+ }
+ match mtype {
+ Manifest | Std => {
+ buf.push_str(options.newline);
+ buf.push_str(cur_padding);
}
- buf.push_str(cur_padding);
- escape_string_json_buf(&field, buf);
- buf.push_str(options.key_val_sep);
- State::push_description(
- || format!("field <{}> manifestification", field.clone()),
- || {
- let value = obj.get(field.clone())?.unwrap();
- manifest_json_ex_buf(&value, buf, cur_padding, options)?;
- Ok(())
- },
- )?;
+ ToString => buf.push(' '),
+ Minify => {}
}
- cur_padding.truncate(old_len);
- if mtype != JsonFormatting::ToString && mtype != JsonFormatting::Minify {
+ escape_string_json_buf(&key, buf);
+ buf.push_str(options.key_val_sep);
+ State::push_description(
+ || format!("field <{key}> manifestification"),
+ || manifest_json_ex_buf(&value, buf, cur_padding, options),
+ )?;
+ }
+
+ cur_padding.truncate(old_len);
+
+ match mtype {
+ Manifest | ToString if !had_fields => {
+ // Empty object as "{ }"
+ buf.push(' ');
+ }
+ Manifest => {
buf.push_str(options.newline);
buf.push_str(cur_padding);
}
- } else if mtype == JsonFormatting::Std {
- buf.push_str(options.newline);
- buf.push_str(options.newline);
- buf.push_str(cur_padding);
- } else if mtype == JsonFormatting::ToString || mtype == JsonFormatting::Manifest {
- buf.push(' ');
+ Std => {
+ if !had_fields {
+ // Stdlib formats empty object as "{\n\n}"
+ buf.push_str(options.newline);
+ }
+ buf.push_str(options.newline);
+ buf.push_str(cur_padding);
+ }
+ Minify | ToString => {}
}
+
buf.push('}');
}
Val::Func(_) => bail!("tried to manifest function"),
@@ -350,7 +381,7 @@
Self {
inner,
c_document_end,
- // Stdlib format always inserts newline at the end
+ // Stdlib format always inserts useless newline at the end
end_newline: true,
}
}
@@ -371,10 +402,13 @@
)
};
if !arr.is_empty() {
- for v in arr.iter() {
- let v = v?;
+ for (i, v) in arr.iter().enumerate() {
+ let v = v.with_description(|| format!("elem <{i}> evaluation"))?;
out.push_str("---\n");
- self.inner.manifest_buf(v, out)?;
+ State::push_description(
+ || format!("elem <{i}> manifestification"),
+ || self.inner.manifest_buf(v, out),
+ )?;
out.push('\n');
}
}
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, BytesArray},10 bail,11 function::{native::NativeDesc, FuncDesc, FuncVal},12 typed::CheckType,13 val::{IndexableVal, StrValue, ThunkMapper},14 ObjValue, ObjValueBuilder, Result, Thunk, Val,15};1617#[derive(Trace)]18struct FromUntyped<K: Trace>(PhantomData<fn() -> K>);19impl<K> ThunkMapper<Val> for FromUntyped<K>20where21 K: Typed + Trace,22{23 type Output = K;2425 fn map(self, from: Val) -> Result<Self::Output> {26 K::from_untyped(from)27 }28}29impl<K: Trace> Default for FromUntyped<K> {30 fn default() -> Self {31 Self(PhantomData)32 }33}3435pub trait TypedObj: Typed {36 fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;37 fn parse(obj: &ObjValue) -> Result<Self>;38 fn into_object(self) -> Result<ObjValue> {39 let mut builder = ObjValueBuilder::new();40 self.serialize(&mut builder)?;41 Ok(builder.build())42 }43}4445pub trait Typed: Sized {46 const TYPE: &'static ComplexValType;47 fn into_untyped(typed: Self) -> Result<Val>;48 fn into_lazy_untyped(typed: Self) -> Thunk<Val> {49 Thunk::from(Self::into_untyped(typed))50 }51 fn from_untyped(untyped: Val) -> Result<Self>;52 fn from_lazy_untyped(lazy: Thunk<Val>) -> Result<Self> {53 Self::from_untyped(lazy.evaluate()?)54 }5556 // Whatever caller should use `into_lazy_untyped` instead of `into_untyped`57 fn provides_lazy() -> bool {58 false59 }6061 // Whatever caller should use `from_lazy_untyped` instead of `from_untyped` when possible62 fn wants_lazy() -> bool {63 false64 }6566 /// Hack to make builtins be able to return non-result values, and make macros able to convert those values to result67 /// This method returns identity in impl Typed for Result, and should not be overriden68 #[doc(hidden)]69 fn into_result(typed: Self) -> Result<Val> {70 let value = Self::into_untyped(typed)?;71 Ok(value)72 }73}7475impl<T> Typed for Thunk<T>76where77 T: Typed + Trace + Clone,78{79 const TYPE: &'static ComplexValType = &ComplexValType::Lazy(T::TYPE);8081 fn into_untyped(typed: Self) -> Result<Val> {82 T::into_untyped(typed.evaluate()?)83 }8485 fn from_untyped(untyped: Val) -> Result<Self> {86 Self::from_lazy_untyped(Thunk::evaluated(untyped))87 }8889 fn provides_lazy() -> bool {90 true91 }9293 fn into_lazy_untyped(inner: Self) -> Thunk<Val> {94 #[derive(Trace)]95 struct IntoUntyped<K: Trace>(PhantomData<fn() -> K>);96 impl<K> ThunkMapper<K> for IntoUntyped<K>97 where98 K: Typed + Trace,99 {100 type Output = Val;101102 fn map(self, from: K) -> Result<Self::Output> {103 K::into_untyped(from)104 }105 }106 impl<K: Trace> Default for IntoUntyped<K> {107 fn default() -> Self {108 Self(PhantomData)109 }110 }111 inner.map(<IntoUntyped<T>>::default())112 }113114 fn wants_lazy() -> bool {115 true116 }117118 fn from_lazy_untyped(inner: Thunk<Val>) -> Result<Self> {119 Ok(inner.map(<FromUntyped<T>>::default()))120 }121}122123const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS + 1)) - 1) as f64;124125macro_rules! impl_int {126 ($($ty:ty)*) => {$(127 impl Typed for $ty {128 const TYPE: &'static ComplexValType =129 &ComplexValType::BoundedNumber(Some(Self::MIN as f64), Some(Self::MAX as f64));130 fn from_untyped(value: Val) -> Result<Self> {131 <Self as Typed>::TYPE.check(&value)?;132 match value {133 Val::Num(n) => {134 #[allow(clippy::float_cmp)]135 if n.trunc() != n {136 bail!(137 "cannot convert number with fractional part to {}",138 stringify!($ty)139 )140 }141 Ok(n as Self)142 }143 _ => unreachable!(),144 }145 }146 #[allow(clippy::cast_lossless)]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 bail!(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 #[allow(clippy::cast_lossless)]204 fn into_untyped(value: Self) -> Result<Val> {205 Ok(Val::Num(value.0 as f64))206 }207 }208 )*};209}210211impl_bounded_int!(212 BoundedI8 = i8213 BoundedI16 = i16214 BoundedI32 = i32215 BoundedI64 = i64216 BoundedUsize = usize217);218219impl Typed for f64 {220 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);221222 fn into_untyped(value: Self) -> Result<Val> {223 Ok(Val::Num(value))224 }225226 fn from_untyped(value: Val) -> Result<Self> {227 <Self as Typed>::TYPE.check(&value)?;228 match value {229 Val::Num(n) => Ok(n),230 _ => unreachable!(),231 }232 }233}234235pub struct PositiveF64(pub f64);236impl Typed for PositiveF64 {237 const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(0.0), None);238239 fn into_untyped(value: Self) -> Result<Val> {240 Ok(Val::Num(value.0))241 }242243 fn from_untyped(value: Val) -> Result<Self> {244 <Self as Typed>::TYPE.check(&value)?;245 match value {246 Val::Num(n) => Ok(Self(n)),247 _ => unreachable!(),248 }249 }250}251impl Typed for usize {252 const TYPE: &'static ComplexValType =253 &ComplexValType::BoundedNumber(Some(0.0), Some(MAX_SAFE_INTEGER));254255 fn into_untyped(value: Self) -> Result<Val> {256 if value > MAX_SAFE_INTEGER as Self {257 bail!("number is too large")258 }259 Ok(Val::Num(value as f64))260 }261262 fn from_untyped(value: Val) -> Result<Self> {263 <Self as Typed>::TYPE.check(&value)?;264 match value {265 Val::Num(n) => {266 #[allow(clippy::float_cmp)]267 if n.trunc() != n {268 bail!("cannot convert number with fractional part to usize")269 }270 Ok(n as Self)271 }272 _ => unreachable!(),273 }274 }275}276277impl Typed for IStr {278 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);279280 fn into_untyped(value: Self) -> Result<Val> {281 Ok(Val::string(value))282 }283284 fn from_untyped(value: Val) -> Result<Self> {285 <Self as Typed>::TYPE.check(&value)?;286 match value {287 Val::Str(s) => Ok(s.into_flat()),288 _ => unreachable!(),289 }290 }291}292293impl Typed for String {294 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);295296 fn into_untyped(value: Self) -> Result<Val> {297 Ok(Val::string(value))298 }299300 fn from_untyped(value: Val) -> Result<Self> {301 <Self as Typed>::TYPE.check(&value)?;302 match value {303 Val::Str(s) => Ok(s.to_string()),304 _ => unreachable!(),305 }306 }307}308309impl Typed for StrValue {310 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);311312 fn into_untyped(value: Self) -> Result<Val> {313 Ok(Val::Str(value))314 }315316 fn from_untyped(value: Val) -> Result<Self> {317 <Self as Typed>::TYPE.check(&value)?;318 match value {319 Val::Str(s) => Ok(s),320 _ => unreachable!(),321 }322 }323}324325impl Typed for char {326 const TYPE: &'static ComplexValType = &ComplexValType::Char;327328 fn into_untyped(value: Self) -> Result<Val> {329 Ok(Val::string(value))330 }331332 fn from_untyped(value: Val) -> Result<Self> {333 <Self as Typed>::TYPE.check(&value)?;334 match value {335 Val::Str(s) => Ok(s.into_flat().chars().next().unwrap()),336 _ => unreachable!(),337 }338 }339}340341impl<T> Typed for Vec<T>342where343 T: Typed,344{345 const TYPE: &'static ComplexValType = &ComplexValType::ArrayRef(T::TYPE);346347 fn into_untyped(value: Self) -> Result<Val> {348 Ok(Val::Arr(349 value350 .into_iter()351 .map(T::into_untyped)352 .collect::<Result<ArrValue>>()?,353 ))354 }355356 fn from_untyped(value: Val) -> Result<Self> {357 let Val::Arr(a) = value else {358 <Self as Typed>::TYPE.check(&value)?;359 unreachable!("typecheck should fail")360 };361 a.iter()362 .map(|r| r.and_then(T::from_untyped))363 .collect::<Result<Self>>()364 }365}366367impl<K: Typed + Ord, V: Typed> Typed for BTreeMap<K, V> {368 const TYPE: &'static ComplexValType = &ComplexValType::AttrsOf(V::TYPE);369370 fn into_untyped(typed: Self) -> Result<Val> {371 let mut out = ObjValueBuilder::with_capacity(typed.len());372 for (k, v) in typed {373 let Some(key) = K::into_untyped(k)?.as_str() else {374 bail!("map key should serialize to string");375 };376 let value = V::into_untyped(v)?;377 out.field(key).value(value);378 }379 Ok(Val::Obj(out.build()))380 }381382 fn from_untyped(value: Val) -> Result<Self> {383 Self::TYPE.check(&value)?;384 let obj = value.as_obj().expect("typecheck should fail");385386 let mut out = Self::new();387 if V::wants_lazy() {388 for key in obj.fields_ex(389 false,390 #[cfg(feature = "exp-preserve-order")]391 false,392 ) {393 let value = obj.get_lazy(key.clone()).expect("field exists");394 let value = V::from_lazy_untyped(value)?;395 let key = K::from_untyped(Val::Str(key.into()))?;396 let _ = out.insert(key, value);397 }398 } else {399 for (key, value) in obj.iter(400 #[cfg(feature = "exp-preserve-order")]401 false,402 ) {403 let key = K::from_untyped(Val::Str(key.into()))?;404 let value = V::from_untyped(value?)?;405 let _ = out.insert(key, value);406 }407 }408 Ok(out)409 }410}411412impl Typed for Val {413 const TYPE: &'static ComplexValType = &ComplexValType::Any;414415 fn into_untyped(typed: Self) -> Result<Val> {416 Ok(typed)417 }418 fn from_untyped(untyped: Val) -> Result<Self> {419 Ok(untyped)420 }421}422423// Hack424#[doc(hidden)]425impl<T> Typed for Result<T>426where427 T: Typed,428{429 const TYPE: &'static ComplexValType = &ComplexValType::Any;430431 fn into_untyped(_typed: Self) -> Result<Val> {432 panic!("do not use this conversion")433 }434435 fn from_untyped(_untyped: Val) -> Result<Self> {436 panic!("do not use this conversion")437 }438439 fn into_result(typed: Self) -> Result<Val> {440 typed.map(T::into_untyped)?441 }442}443444/// Specialization445impl Typed for IBytes {446 const TYPE: &'static ComplexValType =447 &ComplexValType::ArrayRef(&ComplexValType::BoundedNumber(Some(0.0), Some(255.0)));448449 fn into_untyped(value: Self) -> Result<Val> {450 Ok(Val::Arr(ArrValue::bytes(value)))451 }452453 fn from_untyped(value: Val) -> Result<Self> {454 let Val::Arr(a) = &value else {455 <Self as Typed>::TYPE.check(&value)?;456 unreachable!()457 };458 if let Some(bytes) = a.as_any().downcast_ref::<BytesArray>() {459 return Ok(bytes.0.as_slice().into());460 };461 <Self as Typed>::TYPE.check(&value)?;462 // Any::downcast_ref::<ByteArray>(&a);463 let mut out = Vec::with_capacity(a.len());464 for e in a.iter() {465 let r = e?;466 out.push(u8::from_untyped(r)?);467 }468 Ok(out.as_slice().into())469 }470}471472pub struct M1;473impl Typed for M1 {474 const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(-1.0), Some(-1.0));475476 fn into_untyped(_: Self) -> Result<Val> {477 Ok(Val::Num(-1.0))478 }479480 fn from_untyped(value: Val) -> Result<Self> {481 <Self as Typed>::TYPE.check(&value)?;482 Ok(Self)483 }484}485486macro_rules! decl_either {487 ($($name: ident, $($id: ident)*);*) => {$(488 #[derive(Clone)]489 pub enum $name<$($id),*> {490 $($id($id)),*491 }492 impl<$($id),*> Typed for $name<$($id),*>493 where494 $($id: Typed,)*495 {496 const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[$($id::TYPE),*]);497498 fn into_untyped(value: Self) -> Result<Val> {499 match value {$(500 $name::$id(v) => $id::into_untyped(v)501 ),*}502 }503504 fn from_untyped(value: Val) -> Result<Self> {505 $(506 if $id::TYPE.check(&value).is_ok() {507 $id::from_untyped(value).map(Self::$id)508 } else509 )* {510 <Self as Typed>::TYPE.check(&value)?;511 unreachable!()512 }513 }514 }515 )*}516}517decl_either!(518 Either1, A;519 Either2, A B;520 Either3, A B C;521 Either4, A B C D;522 Either5, A B C D E;523 Either6, A B C D E F;524 Either7, A B C D E F G525);526#[macro_export]527macro_rules! Either {528 ($a:ty) => {$crate::typed::Either1<$a>};529 ($a:ty, $b:ty) => {$crate::typed::Either2<$a, $b>};530 ($a:ty, $b:ty, $c:ty) => {$crate::typed::Either3<$a, $b, $c>};531 ($a:ty, $b:ty, $c:ty, $d:ty) => {$crate::typed::Either4<$a, $b, $c, $d>};532 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty) => {$crate::typed::Either5<$a, $b, $c, $d, $e>};533 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty) => {$crate::typed::Either6<$a, $b, $c, $d, $e, $f>};534 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty, $g:ty) => {$crate::typed::Either7<$a, $b, $c, $d, $e, $f, $g>};535}536pub use Either;537538pub type MyType = Either![u32, f64, String];539540impl Typed for ArrValue {541 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);542543 fn into_untyped(value: Self) -> Result<Val> {544 Ok(Val::Arr(value))545 }546547 fn from_untyped(value: Val) -> Result<Self> {548 <Self as Typed>::TYPE.check(&value)?;549 match value {550 Val::Arr(a) => Ok(a),551 _ => unreachable!(),552 }553 }554}555556impl Typed for FuncVal {557 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);558559 fn into_untyped(value: Self) -> Result<Val> {560 Ok(Val::Func(value))561 }562563 fn from_untyped(value: Val) -> Result<Self> {564 <Self as Typed>::TYPE.check(&value)?;565 match value {566 Val::Func(a) => Ok(a),567 _ => unreachable!(),568 }569 }570}571572impl Typed for Cc<FuncDesc> {573 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);574575 fn into_untyped(value: Self) -> Result<Val> {576 Ok(Val::Func(FuncVal::Normal(value)))577 }578579 fn from_untyped(value: Val) -> Result<Self> {580 <Self as Typed>::TYPE.check(&value)?;581 match value {582 Val::Func(FuncVal::Normal(desc)) => Ok(desc),583 Val::Func(_) => bail!("expected normal function, not builtin"),584 _ => unreachable!(),585 }586 }587}588589impl Typed for ObjValue {590 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Obj);591592 fn into_untyped(value: Self) -> Result<Val> {593 Ok(Val::Obj(value))594 }595596 fn from_untyped(value: Val) -> Result<Self> {597 <Self as Typed>::TYPE.check(&value)?;598 match value {599 Val::Obj(a) => Ok(a),600 _ => unreachable!(),601 }602 }603}604605impl Typed for bool {606 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Bool);607608 fn into_untyped(value: Self) -> Result<Val> {609 Ok(Val::Bool(value))610 }611612 fn from_untyped(value: Val) -> Result<Self> {613 <Self as Typed>::TYPE.check(&value)?;614 match value {615 Val::Bool(a) => Ok(a),616 _ => unreachable!(),617 }618 }619}620impl Typed for IndexableVal {621 const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[622 &ComplexValType::Simple(ValType::Arr),623 &ComplexValType::Simple(ValType::Str),624 ]);625626 fn into_untyped(value: Self) -> Result<Val> {627 match value {628 Self::Str(s) => Ok(Val::string(s)),629 Self::Arr(a) => Ok(Val::Arr(a)),630 }631 }632633 fn from_untyped(value: Val) -> Result<Self> {634 <Self as Typed>::TYPE.check(&value)?;635 value.into_indexable()636 }637}638639pub struct Null;640impl Typed for Null {641 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Null);642643 fn into_untyped(_: Self) -> Result<Val> {644 Ok(Val::Null)645 }646647 fn from_untyped(value: Val) -> Result<Self> {648 <Self as Typed>::TYPE.check(&value)?;649 Ok(Self)650 }651}652653pub struct NativeFn<D: NativeDesc>(D::Value);654impl<D: NativeDesc> Deref for NativeFn<D> {655 type Target = D::Value;656657 fn deref(&self) -> &Self::Target {658 &self.0659 }660}661impl<D: NativeDesc> Typed for NativeFn<D> {662 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);663664 fn into_untyped(_typed: Self) -> Result<Val> {665 bail!("can only convert functions from jsonnet to native")666 }667668 fn from_untyped(untyped: Val) -> Result<Self> {669 Ok(Self(670 untyped671 .as_func()672 .expect("shape is checked")673 .into_native::<D>(),674 ))675 }676}crates/jrsonnet-stdlib/src/manifest/toml.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/manifest/toml.rs
+++ b/crates/jrsonnet-stdlib/src/manifest/toml.rs
@@ -4,7 +4,7 @@
bail,
manifest::{escape_string_json_buf, ManifestFormat},
val::ArrValue,
- IStr, ObjValue, Result, Val,
+ IStr, ObjValue, Result, ResultExt, Val, State,
};
pub struct TomlFormat<'s> {
@@ -106,16 +106,15 @@
#[cfg(feature = "exp-bigint")]
Val::BigInt(n) => write!(buf, "{n}").unwrap(),
Val::Arr(a) => {
- if a.is_empty() {
- buf.push_str("[]");
- return Ok(());
- }
+ buf.push('[');
+
+ let mut had_items = false;
for (i, e) in a.iter().enumerate() {
- let e = e?;
+ had_items = true;
+ let e = e.with_description(|| format!("elem <{i}> evaluation"))?;
+
if i != 0 {
buf.push(',');
- } else {
- buf.push('[');
}
if inline {
buf.push(' ');
@@ -124,9 +123,15 @@
buf.push_str(cur_padding);
buf.push_str(&options.padding);
}
- manifest_value(&e, true, buf, "", options)?;
+
+ State::push_description(
+ || format!("elem <{i}> manifestification"),
+ || manifest_value(&e, true, buf, "", options),
+ )?;
}
- if inline {
+
+ if !had_items {
+ } else if inline {
buf.push(' ');
} else {
buf.push('\n');
@@ -135,10 +140,10 @@
buf.push(']');
}
Val::Obj(o) => {
- if o.is_empty() {
- buf.push_str("{}");
- }
- buf.push_str("{ ");
+ o.run_assertions()?;
+ buf.push('{');
+
+ let mut had_fields = false;
for (i, (k, v)) in o
.iter(
#[cfg(feature = "exp-preserve-order")]
@@ -146,15 +151,27 @@
)
.enumerate()
{
- let v = v?;
+ had_fields = true;
+ let v = v.with_description(|| format!("field <{k}> evaluation"))?;
+
if i != 0 {
- buf.push_str(", ");
+ buf.push(',');
}
+ buf.push(' ');
+
escape_key_toml_buf(&k, buf);
buf.push_str(" = ");
- manifest_value(&v, true, buf, "", options)?;
+ State::push_description(
+ || format!("field <{k}> manifestification"),
+ || manifest_value(&v, true, buf, "", options),
+ )?;
}
- buf.push_str(" }");
+
+ if had_fields {
+ buf.push(' ');
+ }
+
+ buf.push('}');
}
Val::Null => {
bail!("tried to manifest null")
crates/jrsonnet-stdlib/src/manifest/xml.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/manifest/xml.rs
+++ b/crates/jrsonnet-stdlib/src/manifest/xml.rs
@@ -1,9 +1,9 @@
use jrsonnet_evaluator::{
bail,
manifest::{ManifestFormat, ToStringFormat},
- typed::{ComplexValType, Either4, Typed, ValType},
+ typed::{ComplexValType, Either2, Either4, Typed, ValType},
val::{ArrValue, IndexableVal},
- Either, ObjValue, Result, ResultExt, Val,
+ Either, ObjValue, Result, ResultExt, Val, State,
};
pub struct XmlJsonmlFormat {
@@ -38,20 +38,22 @@
}
fn from_untyped(untyped: Val) -> Result<Self> {
- let Val::Arr(arr) = untyped else {
- if let Val::Str(s) = untyped {
- return Ok(Self::String(s.to_string()));
- };
- bail!("expected JSONML value (an array or string)");
+ let val = <Either![ArrValue, String]>::from_untyped(untyped)
+ .with_description(|| format!("parsing JSONML value (an array or string)"))?;
+ let arr = match val {
+ Either2::A(a) => a,
+ Either2::B(s) => return Ok(Self::String(s)),
};
if arr.len() < 1 {
- bail!("JSONML value should have tag");
+ bail!("JSONML value should have tag (array length should be >=1)");
};
let tag = String::from_untyped(
arr.get(0)
.with_description(|| "getting JSONML tag")?
.expect("length checked"),
- )?;
+ )
+ .with_description(|| format!("parsing JSONML tag"))?;
+
let (has_attrs, attrs) = if arr.len() >= 2 {
let maybe_attrs = arr
.get(1)
@@ -68,11 +70,16 @@
Ok(Self::Tag {
tag,
attrs,
- children: Typed::from_untyped(Val::Arr(arr.slice(
- Some(if has_attrs { 2 } else { 1 }),
- None,
- None,
- )))?,
+ children: State::push_description(
+ || format!("parsing children"),
+ || {
+ Typed::from_untyped(Val::Arr(arr.slice(
+ Some(if has_attrs { 2 } else { 1 }),
+ None,
+ None,
+ )))
+ },
+ )?,
})
}
}
crates/jrsonnet-stdlib/src/manifest/yaml.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/manifest/yaml.rs
+++ b/crates/jrsonnet-stdlib/src/manifest/yaml.rs
@@ -3,7 +3,7 @@
use jrsonnet_evaluator::{
bail,
manifest::{escape_string_json_buf, ManifestFormat},
- Result, Val,
+ Result, ResultExt, State, Val,
};
pub struct YamlFormat<'s> {
@@ -152,80 +152,87 @@
#[cfg(feature = "exp-bigint")]
Val::BigInt(n) => write!(buf, "{}", *n).unwrap(),
Val::Arr(a) => {
- if a.is_empty() {
- buf.push_str("[]");
- } else {
- for (i, item) in a.iter().enumerate() {
- if i != 0 {
+ let mut had_items = false;
+ for (i, item) in a.iter().enumerate() {
+ had_items = true;
+ let item = item.with_description(|| format!("elem <{i}> evaluation"))?;
+ if i != 0 {
+ buf.push('\n');
+ buf.push_str(cur_padding);
+ }
+ buf.push('-');
+ match &item {
+ Val::Arr(a) if !a.is_empty() => {
buf.push('\n');
buf.push_str(cur_padding);
- }
- let item = item?;
- buf.push('-');
- match &item {
- Val::Arr(a) if !a.is_empty() => {
- buf.push('\n');
- buf.push_str(cur_padding);
- buf.push_str(&options.padding);
- }
- _ => buf.push(' '),
- }
- let extra_padding = match &item {
- Val::Arr(a) => !a.is_empty(),
- Val::Obj(o) => !o.is_empty(),
- _ => false,
- };
- let prev_len = cur_padding.len();
- if extra_padding {
- cur_padding.push_str(&options.padding);
+ buf.push_str(&options.padding);
}
- manifest_yaml_ex_buf(&item, buf, cur_padding, options)?;
- cur_padding.truncate(prev_len);
+ _ => buf.push(' '),
}
+ let extra_padding = match &item {
+ Val::Arr(a) => !a.is_empty(),
+ Val::Obj(o) => !o.is_empty(),
+ _ => false,
+ };
+ let prev_len = cur_padding.len();
+ if extra_padding {
+ cur_padding.push_str(&options.padding);
+ }
+ State::push_description(
+ || format!("elem <{i}> manifestification"),
+ || manifest_yaml_ex_buf(&item, buf, cur_padding, options),
+ )?;
+ cur_padding.truncate(prev_len);
}
+ if !had_items {
+ buf.push_str("[]");
+ }
}
Val::Obj(o) => {
- if o.is_empty() {
- buf.push_str("{}");
- } else {
- for (i, key) in o
- .fields(
- #[cfg(feature = "exp-preserve-order")]
- options.preserve_order,
- )
- .iter()
- .enumerate()
- {
- if i != 0 {
+ let mut had_fields = false;
+ for (i, (key, value)) in o
+ .iter(
+ #[cfg(feature = "exp-preserve-order")]
+ options.preserve_order,
+ )
+ .enumerate()
+ {
+ had_fields = true;
+ let value = value.with_description(|| format!("field <{key}> evaluation"))?;
+ if i != 0 {
+ buf.push('\n');
+ buf.push_str(cur_padding);
+ }
+ if !options.quote_keys && !yaml_needs_quotes(&key) {
+ buf.push_str(&key);
+ } else {
+ escape_string_json_buf(&key, buf);
+ }
+ buf.push(':');
+ let prev_len = cur_padding.len();
+ match &value {
+ Val::Arr(a) if !a.is_empty() => {
buf.push('\n');
buf.push_str(cur_padding);
+ buf.push_str(&options.arr_element_padding);
+ cur_padding.push_str(&options.arr_element_padding);
}
- if !options.quote_keys && !yaml_needs_quotes(key) {
- buf.push_str(key);
- } else {
- escape_string_json_buf(key, buf);
+ Val::Obj(o) if !o.is_empty() => {
+ buf.push('\n');
+ buf.push_str(cur_padding);
+ buf.push_str(&options.padding);
+ cur_padding.push_str(&options.padding);
}
- buf.push(':');
- let prev_len = cur_padding.len();
- let item = o.get(key.clone())?.expect("field exists");
- match &item {
- Val::Arr(a) if !a.is_empty() => {
- buf.push('\n');
- buf.push_str(cur_padding);
- buf.push_str(&options.arr_element_padding);
- cur_padding.push_str(&options.arr_element_padding);
- }
- Val::Obj(o) if !o.is_empty() => {
- buf.push('\n');
- buf.push_str(cur_padding);
- buf.push_str(&options.padding);
- cur_padding.push_str(&options.padding);
- }
- _ => buf.push(' '),
- }
- manifest_yaml_ex_buf(&item, buf, cur_padding, options)?;
- cur_padding.truncate(prev_len);
+ _ => buf.push(' '),
}
+ State::push_description(
+ || format!("field <{key}> manifestification"),
+ || manifest_yaml_ex_buf(&value, buf, cur_padding, options),
+ )?;
+ cur_padding.truncate(prev_len);
+ }
+ if !had_fields {
+ buf.push_str("{}");
}
}
Val::Func(_) => bail!("tried to manifest function"),