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.rsdiffbeforeafterboth1use std::{1use std::{2 fmt::{Debug, Display},2 fmt::{Debug, Display},3 path::PathBuf,3 path::PathBuf, cmp::Ordering,4};4};556use jrsonnet_gcmodule::Trace;6use jrsonnet_gcmodule::Trace;9use jrsonnet_types::ValType;9use jrsonnet_types::ValType;10use thiserror::Error;10use thiserror::Error;111112use crate::{function::CallLocation, stdlib::format::FormatError, typed::TypeLocError};12use crate::{function::CallLocation, stdlib::format::FormatError, typed::TypeLocError, ObjValue};131314fn format_found(list: &[IStr], what: &str) -> String {14pub(crate) fn format_found(list: &[IStr], what: &str) -> String {15 if list.is_empty() {15 if list.is_empty() {16 return String::new();16 return String::new();17 }17 }68 }68 }69}69}7071pub(crate) fn suggest_object_fields(v: &ObjValue, key: IStr) -> Vec<IStr> {72 let mut heap = Vec::new();73 for field in v.fields_ex(74 true,75 #[cfg(feature = "exp-preserve-order")]76 false,77 ) {78 let conf = strsim::jaro_winkler(field.as_str(), key.as_str());79 if conf < 0.8 {80 continue;81 }82 if field.as_str() == key.as_str() {83 panic!("looks like string pooling failure, please write any info regarding this crash to https://github.com/CertainLach/jrsonnet/issues/113, thanks!");84 }85 heap.push((conf, field));86 }87 heap.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));88 heap.into_iter().map(|v| v.1).collect()89}709071type FunctionSignature = Vec<(Option<IStr>, bool)>;91type FunctionSignature = Vec<(Option<IStr>, bool)>;7292348 ($w:ident$(::$i:ident)*$(($($tt:tt)*))?) => {368 ($w:ident$(::$i:ident)*$(($($tt:tt)*))?) => {349 return Err($w$(::$i)*$(($($tt)*))?.into())369 return Err($w$(::$i)*$(($($tt)*))?.into())350 };370 };371 ($w:ident$(::$i:ident)*$({$($tt:tt)*})?) => {372 return Err($w$(::$i)*$({$($tt)*})?.into())373 };351 ($l:literal) => {374 ($l:literal) => {352 return Err($crate::error::ErrorKind::RuntimeError($l.into()).into())375 return Err($crate::error::ErrorKind::RuntimeError($l.into()).into())353 };376 };crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth12use crate::{12use crate::{13 arr::ArrValue,13 arr::ArrValue,14 destructure::evaluate_dest,14 destructure::evaluate_dest,15 error::ErrorKind::*,15 error::{ErrorKind::*, suggest_object_fields},16 evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},16 evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},17 function::{CallLocation, FuncDesc, FuncVal},17 function::{CallLocation, FuncDesc, FuncVal},18 throw,18 throw,466 || format!("field <{key}> access"),466 || format!("field <{key}> access"),467 || match v.get(key.clone().into_flat()) {467 || match v.get(key.clone().into_flat()) {468 Ok(Some(v)) => Ok(v),468 Ok(Some(v)) => Ok(v),469 #[cfg(not(feature = "friendly-errors"))]470 Ok(None) => throw!(NoSuchField(key.clone(), vec![])),471 #[cfg(feature = "friendly-errors")]472 Ok(None) => {469 Ok(None) => {473 let mut heap = Vec::new();474 for field in v.fields_ex(475 true,476 #[cfg(feature = "exp-preserve-order")]477 false,478 ) {479 let conf = strsim::jaro_winkler(470 let suggestions = suggest_object_fields(&v, key.clone().into_flat());480 &field as &str,481 &key.clone().into_flat() as &str,482 );483 if conf < 0.8 {484 continue;485 }486 heap.push((conf, field));487 }488 heap.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));489471490 throw!(NoSuchField(472 throw!(NoSuchField(key.clone().into_flat(), suggestions))491 key.clone().into_flat(),492 heap.into_iter().map(|(_, v)| v).collect()493 ))494 }473 }495 Err(e) => Err(e),474 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