difftreelog
feat refer to subfields of objects in string formatting
in: master
Upstream issue: https://github.com/google/jsonnet/pull/1011
3 files changed
crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -1,6 +1,6 @@
use std::{
fmt::{Debug, Display},
- path::PathBuf,
+ path::PathBuf, cmp::Ordering,
};
use jrsonnet_gcmodule::Trace;
@@ -9,9 +9,9 @@
use jrsonnet_types::ValType;
use thiserror::Error;
-use crate::{function::CallLocation, stdlib::format::FormatError, typed::TypeLocError};
+use crate::{function::CallLocation, stdlib::format::FormatError, typed::TypeLocError, ObjValue};
-fn format_found(list: &[IStr], what: &str) -> String {
+pub(crate) fn format_found(list: &[IStr], what: &str) -> String {
if list.is_empty() {
return String::new();
}
@@ -68,6 +68,26 @@
}
}
+pub(crate) fn suggest_object_fields(v: &ObjValue, key: IStr) -> Vec<IStr> {
+ let mut heap = Vec::new();
+ for field in v.fields_ex(
+ true,
+ #[cfg(feature = "exp-preserve-order")]
+ false,
+ ) {
+ let conf = strsim::jaro_winkler(field.as_str(), key.as_str());
+ if conf < 0.8 {
+ continue;
+ }
+ if field.as_str() == key.as_str() {
+ panic!("looks like string pooling failure, please write any info regarding this crash to https://github.com/CertainLach/jrsonnet/issues/113, thanks!");
+ }
+ heap.push((conf, field));
+ }
+ heap.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));
+ heap.into_iter().map(|v| v.1).collect()
+}
+
type FunctionSignature = Vec<(Option<IStr>, bool)>;
/// Possible errors
@@ -348,6 +368,9 @@
($w:ident$(::$i:ident)*$(($($tt:tt)*))?) => {
return Err($w$(::$i)*$(($($tt)*))?.into())
};
+ ($w:ident$(::$i:ident)*$({$($tt:tt)*})?) => {
+ return Err($w$(::$i)*$({$($tt)*})?.into())
+ };
($l:literal) => {
return Err($crate::error::ErrorKind::RuntimeError($l.into()).into())
};
crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -12,7 +12,7 @@
use crate::{
arr::ArrValue,
destructure::evaluate_dest,
- error::ErrorKind::*,
+ error::{ErrorKind::*, suggest_object_fields},
evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},
function::{CallLocation, FuncDesc, FuncVal},
throw,
@@ -466,31 +466,10 @@
|| format!("field <{key}> access"),
|| match v.get(key.clone().into_flat()) {
Ok(Some(v)) => Ok(v),
- #[cfg(not(feature = "friendly-errors"))]
- Ok(None) => throw!(NoSuchField(key.clone(), vec![])),
- #[cfg(feature = "friendly-errors")]
Ok(None) => {
- let mut heap = Vec::new();
- for field in v.fields_ex(
- true,
- #[cfg(feature = "exp-preserve-order")]
- false,
- ) {
- let conf = strsim::jaro_winkler(
- &field as &str,
- &key.clone().into_flat() as &str,
- );
- if conf < 0.8 {
- continue;
- }
- heap.push((conf, field));
- }
- heap.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));
+ let suggestions = suggest_object_fields(&v, key.clone().into_flat());
- throw!(NoSuchField(
- key.clone().into_flat(),
- heap.into_iter().map(|(_, v)| v).collect()
- ))
+ throw!(NoSuchField(key.clone().into_flat(), suggestions))
}
Err(e) => Err(e),
},
crates/jrsonnet-evaluator/src/stdlib/format.rsdiffbeforeafterboth7use thiserror::Error;7use thiserror::Error;889use crate::{error::ErrorKind::*, throw, typed::Typed, Error, ObjValue, Result, Val};9use crate::{10 error::{format_found, suggest_object_fields, ErrorKind::*},11 throw,12 typed::Typed,13 Error, ObjValue, Result, Val,14};101511#[derive(Debug, Clone, Error, Trace)]16#[derive(Debug, Clone, Error, Trace)]25 #[error("no such format field: {0}")]30 #[error("no such format field: {0}")]26 NoSuchFormatField(IStr),31 NoSuchFormatField(IStr),3233 #[error("expected subfield <{0}> to be an object, got {1} instead")]34 SubfieldDidntYieldAnObject(IStr, ValType),35 #[error("subfield not found: <[{full}]{current}>{}", format_found(.found, "subfield"))]36 SubfieldNotFound {37 current: IStr,38 full: IStr,39 found: Box<Vec<IStr>>,40 },27}41}284229impl From<FormatError> for Error {43impl From<FormatError> for Error {691 Ok(out)705 Ok(out)692}706}707708fn get_dotted_field(obj: ObjValue, field: &str) -> Result<Val> {709 let mut current = Val::Obj(obj);710 let mut name_offset = 0;711 for component in field.split('.') {712 let end_offset = name_offset + component.len();713 current = if let Val::Obj(obj) = current {714 if let Some(value) = obj.get(component.into())? {715 value716 } else {717 let current = &field[name_offset..end_offset];718 let full = &field[..name_offset];719 let found = Box::new(suggest_object_fields(&obj, current.into()));720 throw!(SubfieldNotFound {721 current: current.into(),722 full: full.into(),723 found,724 })725 }726 } else {727 // No underflow may happen, initially we always start with an object728 let subfield = &field[..name_offset - 1];729 throw!(SubfieldDidntYieldAnObject(730 subfield.into(),731 current.value_type()732 ));733 };734 name_offset = end_offset + 1;735 }736 Ok(current)737}693738694pub fn format_obj(str: &str, values: &ObjValue) -> Result<String> {739pub fn format_obj(str: &str, values: &ObjValue) -> Result<String> {695 let codes = parse_codes(str)?;740 let codes = parse_codes(str)?;726 if let Some(v) = values.get(f.clone())? {771 if let Some(v) = values.get(f.clone())? {727 v772 v728 } else {773 } else {729 throw!(NoSuchFormatField(f));774 get_dotted_field(values.clone(), &f)?730 }775 }731 };776 };732777