git.delta.rocks / jrsonnet / refs/commits / cc55b2dd2d98

difftreelog

feat add description stacktrace frames for all formats

Yaroslav Bolyukin2024-06-18parent: #912ece7.patch.diff
in: master

5 files changed

modifiedcrates/jrsonnet-evaluator/src/manifest.rsdiffbeforeafterboth
183 cur_padding: &mut String,183 cur_padding: &mut String,
184 options: &JsonFormat<'_>,184 options: &JsonFormat<'_>,
185) -> Result<()> {185) -> Result<()> {
186 use JsonFormatting::*;
187
186 let mtype = options.mtype;188 let mtype = options.mtype;
187 match val {189 match val {
218 }220 }
219 Val::Arr(items) => {221 Val::Arr(items) => {
220 buf.push('[');222 buf.push('[');
221 if !items.is_empty() {
222 if mtype != JsonFormatting::ToString && mtype != JsonFormatting::Minify {
223 buf.push_str(options.newline);
224 }
225223
226 let old_len = cur_padding.len();224 let old_len = cur_padding.len();
227 cur_padding.push_str(&options.padding);225 cur_padding.push_str(&options.padding);
226
227 let mut had_items = false;
228 for (i, item) in items.iter().enumerate() {228 for (i, item) in items.iter().enumerate() {
229 had_items = true;
230 let item = item.with_description(|| format!("elem <{i}> evaluation"))?;
231
229 if i != 0 {232 if i != 0 {
233 buf.push(',');
234 }
235 match mtype {
236 Manifest | Std => {
230 buf.push(',');237 buf.push_str(options.newline);
231 if mtype == JsonFormatting::ToString {238 buf.push_str(cur_padding);
239 }
232 buf.push(' ');240 ToString => buf.push(' '),
233 } else if mtype != JsonFormatting::Minify {241 Minify => {}
234 buf.push_str(options.newline);
235 }
236 }242 };
243
237 buf.push_str(cur_padding);244 buf.push_str(cur_padding);
238 manifest_json_ex_buf(&item?, buf, cur_padding, options)
239 .with_description(|| format!("elem <{i}> manifestification"))?;245 State::push_description(
246 || format!("elem <{i}> manifestification"),
247 || manifest_json_ex_buf(&item, buf, cur_padding, options),
248 )?;
240 }249 }
250
241 cur_padding.truncate(old_len);251 cur_padding.truncate(old_len);
242252
253 match mtype {
254 Manifest | ToString if !had_items => {
255 // Empty array as "[ ]"
256 buf.push(' ');
257 }
243 if mtype != JsonFormatting::ToString && mtype != JsonFormatting::Minify {258 Manifest => {
244 buf.push_str(options.newline);259 buf.push_str(options.newline);
245 buf.push_str(cur_padding);260 buf.push_str(cur_padding);
246 }261 }
247 } else if mtype == JsonFormatting::Std {262 Std => {
263 if !had_items {
264 // Stdlib formats empty array as "[\n\n]"
248 buf.push_str(options.newline);265 buf.push_str(options.newline);
266 }
249 buf.push_str(options.newline);267 buf.push_str(options.newline);
250 buf.push_str(cur_padding);268 buf.push_str(cur_padding);
251 } else if mtype == JsonFormatting::ToString || mtype == JsonFormatting::Manifest {269 }
252 buf.push(' ');270 Minify | ToString => {}
253 }271 }
272
254 buf.push(']');273 buf.push(']');
255 }274 }
256 Val::Obj(obj) => {275 Val::Obj(obj) => {
257 obj.run_assertions()?;276 obj.run_assertions()?;
258 buf.push('{');277 buf.push('{');
259 let fields = obj.fields(
260 #[cfg(feature = "exp-preserve-order")]
261 options.preserve_order,
262 );
263 if !fields.is_empty() {
264 if mtype != JsonFormatting::ToString && mtype != JsonFormatting::Minify {
265 buf.push_str(options.newline);
266 }
267278
268 let old_len = cur_padding.len();279 let old_len = cur_padding.len();
269 cur_padding.push_str(&options.padding);280 cur_padding.push_str(&options.padding);
281
282 let mut had_fields = false;
270 for (i, field) in fields.into_iter().enumerate() {283 for (i, (key, value)) in obj
284 .iter(
285 #[cfg(feature = "exp-preserve-order")]
286 options.preserve_order,
287 )
288 .enumerate()
289 {
290 had_fields = true;
291 let value = value.with_description(|| format!("field <{key}> evaluation"))?;
292
271 if i != 0 {293 if i != 0 {
272 buf.push(',');294 buf.push(',');
273 if mtype == JsonFormatting::ToString {
274 buf.push(' ');
275 } else if mtype != JsonFormatting::Minify {
276 buf.push_str(options.newline);
277 }
278 }295 }
296 match mtype {
297 Manifest | Std => {
298 buf.push_str(options.newline);
279 buf.push_str(cur_padding);299 buf.push_str(cur_padding);
300 }
301 ToString => buf.push(' '),
302 Minify => {}
303 }
304
280 escape_string_json_buf(&field, buf);305 escape_string_json_buf(&key, buf);
281 buf.push_str(options.key_val_sep);306 buf.push_str(options.key_val_sep);
282 State::push_description(307 State::push_description(
283 || format!("field <{}> manifestification", field.clone()),308 || format!("field <{key}> manifestification"),
284 || {309 || manifest_json_ex_buf(&value, buf, cur_padding, options),
285 let value = obj.get(field.clone())?.unwrap();
286 manifest_json_ex_buf(&value, buf, cur_padding, options)?;
287 Ok(())
288 },
289 )?;310 )?;
290 }311 }
312
291 cur_padding.truncate(old_len);313 cur_padding.truncate(old_len);
292314
315 match mtype {
316 Manifest | ToString if !had_fields => {
317 // Empty object as "{ }"
318 buf.push(' ');
319 }
293 if mtype != JsonFormatting::ToString && mtype != JsonFormatting::Minify {320 Manifest => {
294 buf.push_str(options.newline);321 buf.push_str(options.newline);
295 buf.push_str(cur_padding);322 buf.push_str(cur_padding);
296 }323 }
297 } else if mtype == JsonFormatting::Std {324 Std => {
325 if !had_fields {
326 // Stdlib formats empty object as "{\n\n}"
298 buf.push_str(options.newline);327 buf.push_str(options.newline);
328 }
299 buf.push_str(options.newline);329 buf.push_str(options.newline);
300 buf.push_str(cur_padding);330 buf.push_str(cur_padding);
301 } else if mtype == JsonFormatting::ToString || mtype == JsonFormatting::Manifest {331 }
302 buf.push(' ');332 Minify | ToString => {}
303 }333 }
334
304 buf.push('}');335 buf.push('}');
305 }336 }
350 Self {381 Self {
351 inner,382 inner,
352 c_document_end,383 c_document_end,
353 // Stdlib format always inserts newline at the end384 // Stdlib format always inserts useless newline at the end
354 end_newline: true,385 end_newline: true,
355 }386 }
356 }387 }
371 )402 )
372 };403 };
373 if !arr.is_empty() {404 if !arr.is_empty() {
374 for v in arr.iter() {405 for (i, v) in arr.iter().enumerate() {
375 let v = v?;406 let v = v.with_description(|| format!("elem <{i}> evaluation"))?;
376 out.push_str("---\n");407 out.push_str("---\n");
408 State::push_description(
409 || format!("elem <{i}> manifestification"),
377 self.inner.manifest_buf(v, out)?;410 || self.inner.manifest_buf(v, out),
411 )?;
378 out.push('\n');412 out.push('\n');
379 }413 }
380 }414 }
modifiedcrates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -11,7 +11,7 @@
 	function::{native::NativeDesc, FuncDesc, FuncVal},
 	typed::CheckType,
 	val::{IndexableVal, StrValue, ThunkMapper},
-	ObjValue, ObjValueBuilder, Result, Thunk, Val,
+	ObjValue, ObjValueBuilder, Result, ResultExt, Thunk, Val,
 };
 
 #[derive(Trace)]
@@ -359,7 +359,12 @@
 			unreachable!("typecheck should fail")
 		};
 		a.iter()
-			.map(|r| r.and_then(T::from_untyped))
+			.enumerate()
+			.map(|(i, r)| {
+				r.and_then(|t| {
+					T::from_untyped(t).with_description(|| format!("parsing elem <{i}>"))
+				})
+			})
 			.collect::<Result<Self>>()
 	}
 }
modifiedcrates/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")
modifiedcrates/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,
+					)))
+				},
+			)?,
 		})
 	}
 }
modifiedcrates/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"),