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

difftreelog

feat refer to subfields of objects in string formatting

Yaroslav Bolyukin2023-06-14parent: #aeb8b35.patch.diff
in: master
Upstream issue: https://github.com/google/jsonnet/pull/1011

3 files changed

modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
1use std::{1use std::{
2 fmt::{Debug, Display},2 fmt::{Debug, Display},
3 path::PathBuf,3 path::PathBuf, cmp::Ordering,
4};4};
55
6use jrsonnet_gcmodule::Trace;6use jrsonnet_gcmodule::Trace;
9use jrsonnet_types::ValType;9use jrsonnet_types::ValType;
10use thiserror::Error;10use thiserror::Error;
1111
12use crate::{function::CallLocation, stdlib::format::FormatError, typed::TypeLocError};12use crate::{function::CallLocation, stdlib::format::FormatError, typed::TypeLocError, ObjValue};
1313
14fn 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}
70
71pub(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}
7090
71type FunctionSignature = Vec<(Option<IStr>, bool)>;91type FunctionSignature = Vec<(Option<IStr>, bool)>;
7292
348 ($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 };
modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
12use 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));
489471
490 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),
modifiedcrates/jrsonnet-evaluator/src/stdlib/format.rsdiffbeforeafterboth
7use thiserror::Error;7use thiserror::Error;
88
9use 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};
1015
11#[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),
32
33 #[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}
2842
29impl From<FormatError> for Error {43impl From<FormatError> for Error {
691 Ok(out)705 Ok(out)
692}706}
707
708fn 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 value
716 } 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 object
728 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}
693738
694pub 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 v
728 } else {773 } else {
729 throw!(NoSuchFormatField(f));774 get_dotted_field(values.clone(), &f)?
730 }775 }
731 };776 };
732777