Skip to content

Commit

Permalink
feat: add description stacktrace frames for all formats
Browse files Browse the repository at this point in the history
  • Loading branch information
CertainLach committed May 4, 2024
1 parent ea99ae6 commit 731c7c0
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 163 deletions.
166 changes: 100 additions & 66 deletions crates/jrsonnet-evaluator/src/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ fn manifest_json_ex_buf(
cur_padding: &mut String,
options: &JsonFormat<'_>,
) -> Result<()> {
use JsonFormatting::*;

let mtype = options.mtype;
match val {
Val::Bool(v) => {
Expand Down Expand Up @@ -218,89 +220,118 @@ fn manifest_json_ex_buf(
}
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);
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);
}
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 => {}
};

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"),
Expand Down Expand Up @@ -350,7 +381,7 @@ impl<I> YamlStreamFormat<I> {
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,
}
}
Expand All @@ -371,10 +402,13 @@ impl<I: ManifestFormat> ManifestFormat for YamlStreamFormat<I> {
)
};
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');
}
}
Expand Down
9 changes: 7 additions & 2 deletions crates/jrsonnet-evaluator/src/typed/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::{
function::{native::NativeDesc, FuncDesc, FuncVal},
typed::CheckType,
val::{IndexableVal, StrValue, ThunkMapper},
ObjValue, ObjValueBuilder, Result, Thunk, Val,
ObjValue, ObjValueBuilder, Result, ResultExt, Thunk, Val,
};

#[derive(Trace)]
Expand Down Expand Up @@ -357,7 +357,12 @@ where
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>>()
}
}
Expand Down
53 changes: 35 additions & 18 deletions crates/jrsonnet-stdlib/src/manifest/toml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use jrsonnet_evaluator::{
bail,
manifest::{escape_string_json_buf, ManifestFormat},
val::ArrValue,
IStr, ObjValue, Result, Val,
IStr, ObjValue, Result, ResultExt, Val, State,
};

pub struct TomlFormat<'s> {
Expand Down Expand Up @@ -106,16 +106,15 @@ fn manifest_value(
#[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(' ');
Expand All @@ -124,9 +123,15 @@ fn manifest_value(
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');
Expand All @@ -135,26 +140,38 @@ fn manifest_value(
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")]
options.preserve_order,
)
.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")
Expand Down
35 changes: 21 additions & 14 deletions crates/jrsonnet-stdlib/src/manifest/xml.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use jrsonnet_evaluator::{
bail,
manifest::{ManifestFormat, ToStringFormat},
typed::{ComplexValType, Either4, Typed, ValType},
typed::{ComplexValType, Either2, Either4, Typed, ValType},

Check warning on line 4 in crates/jrsonnet-stdlib/src/manifest/xml.rs

View workflow job for this annotation

GitHub Actions / clippy

unused imports: `Either4`, `IndexableVal`

warning: unused imports: `Either4`, `IndexableVal` --> crates/jrsonnet-stdlib/src/manifest/xml.rs:4:35 | 4 | typed::{ComplexValType, Either2, Either4, Typed, ValType}, | ^^^^^^^ 5 | val::{ArrValue, IndexableVal}, | ^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default

Check warning on line 4 in crates/jrsonnet-stdlib/src/manifest/xml.rs

View workflow job for this annotation

GitHub Actions / Test Suite

unused imports: `Either4`, `IndexableVal`
val::{ArrValue, IndexableVal},
Either, ObjValue, Result, ResultExt, Val,
Either, ObjValue, Result, ResultExt, Val, State,
};

pub struct XmlJsonmlFormat {
Expand Down Expand Up @@ -38,20 +38,22 @@ impl Typed for JSONMLValue {
}

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)"))?;

Check warning on line 42 in crates/jrsonnet-stdlib/src/manifest/xml.rs

View workflow job for this annotation

GitHub Actions / clippy

useless use of `format!`

warning: useless use of `format!` --> crates/jrsonnet-stdlib/src/manifest/xml.rs:42:25 | 42 | .with_description(|| format!("parsing JSONML value (an array or string)"))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"parsing JSONML value (an array or string)".to_string()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_format = note: `-W clippy::useless-format` implied by `-W clippy::all` = help: to override `-W clippy::all` add `#[allow(clippy::useless_format)]`
let arr = match val {
Either2::A(a) => a,
Either2::B(s) => return Ok(Self::String(s)),
};
if arr.len() < 1 {

Check warning on line 47 in crates/jrsonnet-stdlib/src/manifest/xml.rs

View workflow job for this annotation

GitHub Actions / clippy

length comparison to one

warning: length comparison to one --> crates/jrsonnet-stdlib/src/manifest/xml.rs:47:6 | 47 | if arr.len() < 1 { | ^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `arr.is_empty()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#len_zero = note: `-W clippy::len-zero` implied by `-W clippy::all` = help: to override `-W clippy::all` add `#[allow(clippy::len_zero)]`
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"))?;

Check warning on line 55 in crates/jrsonnet-stdlib/src/manifest/xml.rs

View workflow job for this annotation

GitHub Actions / clippy

useless use of `format!`

warning: useless use of `format!` --> crates/jrsonnet-stdlib/src/manifest/xml.rs:55:24 | 55 | .with_description(|| format!("parsing JSONML tag"))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"parsing JSONML tag".to_string()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_format

let (has_attrs, attrs) = if arr.len() >= 2 {
let maybe_attrs = arr
.get(1)
Expand All @@ -68,11 +70,16 @@ impl Typed for JSONMLValue {
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"),

Check warning on line 74 in crates/jrsonnet-stdlib/src/manifest/xml.rs

View workflow job for this annotation

GitHub Actions / clippy

useless use of `format!`

warning: useless use of `format!` --> crates/jrsonnet-stdlib/src/manifest/xml.rs:74:8 | 74 | || format!("parsing children"), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"parsing children".to_string()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_format
|| {
Typed::from_untyped(Val::Arr(arr.slice(
Some(if has_attrs { 2 } else { 1 }),
None,
None,
)))
},
)?,
})
}
}
Expand Down
Loading

0 comments on commit 731c7c0

Please sign in to comment.