difftreelog
style use let-else
in: master
7 files changed
crates/jrsonnet-evaluator/src/evaluate/destructure.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/destructure.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/destructure.rs
@@ -45,9 +45,8 @@
fn get(self: Box<Self>) -> Result<Self::Output> {
let v = self.parent.evaluate()?;
- let arr = match v {
- Val::Arr(a) => a,
- _ => throw!("expected array"),
+ let Val::Arr(arr) = v else {
+ throw!("expected array");
};
if !self.has_rest {
if arr.len() != self.min_len {
@@ -176,9 +175,8 @@
fn get(self: Box<Self>) -> Result<Self::Output> {
let v = self.parent.evaluate()?;
- let obj = match v {
- Val::Obj(o) => o,
- _ => throw!("expected object"),
+ let Val::Obj(obj) = v else {
+ throw!("expected object");
};
for field in &self.field_names {
if !obj.has_field_ex(field.clone(), true) {
crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth1use std::{cmp::Ordering, rc::Rc};23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::IStr;5use jrsonnet_parser::{6 ArgsDesc, AssertStmt, BindSpec, CompSpec, Expr, FieldMember, FieldName, ForSpecData,7 IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc,8};9use jrsonnet_types::ValType;1011use crate::{12 destructure::evaluate_dest,13 error::Error::*,14 evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},15 function::{CallLocation, FuncDesc, FuncVal},16 tb, throw,17 typed::Typed,18 val::{ArrValue, CachedUnbound, IndexableVal, Thunk, ThunkValue},19 Context, GcHashMap, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result, State,20 Unbound, Val,21};22pub mod destructure;23pub mod operator;2425pub fn evaluate_method(ctx: Context, name: IStr, params: ParamsDesc, body: LocExpr) -> Val {26 Val::Func(FuncVal::Normal(Cc::new(FuncDesc {27 name,28 ctx,29 params,30 body,31 })))32}3334pub fn evaluate_field_name(ctx: Context, field_name: &FieldName) -> Result<Option<IStr>> {35 Ok(match field_name {36 FieldName::Fixed(n) => Some(n.clone()),37 FieldName::Dyn(expr) => State::push(38 CallLocation::new(&expr.1),39 || "evaluating field name".to_string(),40 || {41 let value = evaluate(ctx, expr)?;42 if matches!(value, Val::Null) {43 Ok(None)44 } else {45 Ok(Some(IStr::from_untyped(value)?))46 }47 },48 )?,49 })50}5152pub fn evaluate_comp(53 ctx: Context,54 specs: &[CompSpec],55 callback: &mut impl FnMut(Context) -> Result<()>,56) -> Result<()> {57 match specs.get(0) {58 None => callback(ctx)?,59 Some(CompSpec::IfSpec(IfSpecData(cond))) => {60 if bool::from_untyped(evaluate(ctx.clone(), cond)?)? {61 evaluate_comp(ctx, &specs[1..], callback)?;62 }63 }64 Some(CompSpec::ForSpec(ForSpecData(var, expr))) => match evaluate(ctx.clone(), expr)? {65 Val::Arr(list) => {66 for item in list.iter() {67 evaluate_comp(68 ctx.clone().with_var(var.clone(), item?.clone()),69 &specs[1..],70 callback,71 )?;72 }73 }74 _ => throw!(InComprehensionCanOnlyIterateOverArray),75 },76 }77 Ok(())78}7980trait CloneableUnbound<T>: Unbound<Bound = T> + Clone {}8182fn evaluate_object_locals(83 fctx: Pending<Context>,84 locals: Rc<Vec<BindSpec>>,85) -> impl CloneableUnbound<Context> {86 #[derive(Trace, Clone)]87 struct UnboundLocals {88 fctx: Pending<Context>,89 locals: Rc<Vec<BindSpec>>,90 }91 impl CloneableUnbound<Context> for UnboundLocals {}92 impl Unbound for UnboundLocals {93 type Bound = Context;9495 fn bind(&self, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<Context> {96 let fctx = Context::new_future();97 let mut new_bindings = GcHashMap::new();98 for b in self.locals.iter() {99 evaluate_dest(b, fctx.clone(), &mut new_bindings)?;100 }101102 let ctx = self.fctx.unwrap();103 let new_dollar = ctx.dollar().clone().or_else(|| this.clone());104105 let ctx = ctx106 .extend(new_bindings, new_dollar, sup, this)107 .into_future(fctx);108109 Ok(ctx)110 }111 }112113 UnboundLocals { fctx, locals }114}115116#[allow(clippy::too_many_lines)]117pub fn evaluate_member_list_object(ctx: Context, members: &[Member]) -> Result<ObjValue> {118 let mut builder = ObjValueBuilder::new();119 let locals = Rc::new(120 members121 .iter()122 .filter_map(|m| match m {123 Member::BindStmt(bind) => Some(bind.clone()),124 _ => None,125 })126 .collect::<Vec<_>>(),127 );128129 let fctx = Context::new_future();130131 // We have single context for all fields, so we can cache binds132 let uctx = CachedUnbound::new(evaluate_object_locals(fctx.clone(), locals));133134 for member in members.iter() {135 match member {136 Member::Field(FieldMember {137 name,138 plus,139 params: None,140 visibility,141 value,142 }) => {143 #[derive(Trace)]144 struct UnboundValue<B: Trace> {145 uctx: B,146 value: LocExpr,147 name: IStr,148 }149 impl<B: Unbound<Bound = Context>> Unbound for UnboundValue<B> {150 type Bound = Thunk<Val>;151 fn bind(152 &self,153 sup: Option<ObjValue>,154 this: Option<ObjValue>,155 ) -> Result<Thunk<Val>> {156 Ok(Thunk::evaluated(evaluate_named(157 self.uctx.bind(sup, this)?,158 &self.value,159 self.name.clone(),160 )?))161 }162 }163164 let name = evaluate_field_name(ctx.clone(), name)?;165 let name = if let Some(name) = name {166 name167 } else {168 continue;169 };170171 builder172 .member(name.clone())173 .with_add(*plus)174 .with_visibility(*visibility)175 .with_location(value.1.clone())176 .bindable(tb!(UnboundValue {177 uctx: uctx.clone(),178 value: value.clone(),179 name: name.clone()180 }))?;181 }182 Member::Field(FieldMember {183 name,184 params: Some(params),185 value,186 ..187 }) => {188 #[derive(Trace)]189 struct UnboundMethod<B: Trace> {190 uctx: B,191 value: LocExpr,192 params: ParamsDesc,193 name: IStr,194 }195 impl<B: Unbound<Bound = Context>> Unbound for UnboundMethod<B> {196 type Bound = Thunk<Val>;197 fn bind(198 &self,199 sup: Option<ObjValue>,200 this: Option<ObjValue>,201 ) -> Result<Thunk<Val>> {202 Ok(Thunk::evaluated(evaluate_method(203 self.uctx.bind(sup, this)?,204 self.name.clone(),205 self.params.clone(),206 self.value.clone(),207 )))208 }209 }210211 let name = if let Some(name) = evaluate_field_name(ctx.clone(), name)? {212 name213 } else {214 continue;215 };216217 builder218 .member(name.clone())219 .hide()220 .with_location(value.1.clone())221 .bindable(tb!(UnboundMethod {222 uctx: uctx.clone(),223 value: value.clone(),224 params: params.clone(),225 name: name.clone()226 }))?;227 }228 Member::BindStmt(_) => {}229 Member::AssertStmt(stmt) => {230 #[derive(Trace)]231 struct ObjectAssert<B: Trace> {232 uctx: B,233 assert: AssertStmt,234 }235 impl<B: Unbound<Bound = Context>> ObjectAssertion for ObjectAssert<B> {236 fn run(&self, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<()> {237 let ctx = self.uctx.bind(sup, this)?;238 evaluate_assert(ctx, &self.assert)239 }240 }241 builder.assert(tb!(ObjectAssert {242 uctx: uctx.clone(),243 assert: stmt.clone(),244 }));245 }246 }247 }248 let this = builder.build();249 fctx.fill(ctx.extend(GcHashMap::new(), None, None, Some(this.clone())));250 Ok(this)251}252253pub fn evaluate_object(ctx: Context, object: &ObjBody) -> Result<ObjValue> {254 Ok(match object {255 ObjBody::MemberList(members) => evaluate_member_list_object(ctx, members)?,256 ObjBody::ObjComp(obj) => {257 let mut builder = ObjValueBuilder::new();258 let locals = Rc::new(259 obj.pre_locals260 .iter()261 .chain(obj.post_locals.iter())262 .cloned()263 .collect::<Vec<_>>(),264 );265 let mut ctxs = vec![];266 evaluate_comp(ctx, &obj.compspecs, &mut |ctx| {267 let key = evaluate(ctx.clone(), &obj.key)?;268 let fctx = Context::new_future();269 ctxs.push((ctx, fctx.clone()));270 let uctx = evaluate_object_locals(fctx, locals.clone());271272 match key {273 Val::Null => {}274 Val::Str(n) => {275 #[derive(Trace)]276 struct UnboundValue<B: Trace> {277 uctx: B,278 value: LocExpr,279 }280 impl<B: Unbound<Bound = Context>> Unbound for UnboundValue<B> {281 type Bound = Thunk<Val>;282 fn bind(283 &self,284 sup: Option<ObjValue>,285 this: Option<ObjValue>,286 ) -> Result<Thunk<Val>> {287 Ok(Thunk::evaluated(evaluate(288 self.uctx.bind(sup, this.clone())?.extend(289 GcHashMap::new(),290 None,291 None,292 this,293 ),294 &self.value,295 )?))296 }297 }298 builder299 .member(n)300 .with_location(obj.value.1.clone())301 .with_add(obj.plus)302 .bindable(tb!(UnboundValue {303 uctx,304 value: obj.value.clone(),305 }))?;306 }307 v => throw!(FieldMustBeStringGot(v.value_type())),308 }309310 Ok(())311 })?;312313 let this = builder.build();314 for (ctx, fctx) in ctxs {315 let _ctx = ctx316 .extend(GcHashMap::new(), None, None, Some(this.clone()))317 .into_future(fctx);318 }319 this320 }321 })322}323324pub fn evaluate_apply(325 ctx: Context,326 value: &LocExpr,327 args: &ArgsDesc,328 loc: CallLocation<'_>,329 tailstrict: bool,330) -> Result<Val> {331 let value = evaluate(ctx.clone(), value)?;332 Ok(match value {333 Val::Func(f) => {334 let body = || f.evaluate(ctx, loc, args, tailstrict);335 if tailstrict {336 body()?337 } else {338 State::push(loc, || format!("function <{}> call", f.name()), body)?339 }340 }341 v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type())),342 })343}344345pub fn evaluate_assert(ctx: Context, assertion: &AssertStmt) -> Result<()> {346 let value = &assertion.0;347 let msg = &assertion.1;348 let assertion_result = State::push(349 CallLocation::new(&value.1),350 || "assertion condition".to_owned(),351 || bool::from_untyped(evaluate(ctx.clone(), value)?),352 )?;353 if !assertion_result {354 State::push(355 CallLocation::new(&value.1),356 || "assertion failure".to_owned(),357 || {358 if let Some(msg) = msg {359 throw!(AssertionFailed(evaluate(ctx, msg)?.to_string()?));360 }361 throw!(AssertionFailed(Val::Null.to_string()?));362 },363 )?;364 }365 Ok(())366}367368pub fn evaluate_named(ctx: Context, expr: &LocExpr, name: IStr) -> Result<Val> {369 use Expr::*;370 let LocExpr(raw_expr, _loc) = expr;371 Ok(match &**raw_expr {372 Function(params, body) => evaluate_method(ctx, name, params.clone(), body.clone()),373 _ => evaluate(ctx, expr)?,374 })375}376377#[allow(clippy::too_many_lines)]378pub fn evaluate(ctx: Context, expr: &LocExpr) -> Result<Val> {379 use Expr::*;380 let LocExpr(expr, loc) = expr;381 // let bp = with_state(|s| s.0.stop_at.borrow().clone());382 Ok(match &**expr {383 Literal(LiteralType::This) => {384 Val::Obj(ctx.this().clone().ok_or(CantUseSelfOutsideOfObject)?)385 }386 Literal(LiteralType::Super) => Val::Obj(387 ctx.super_obj().clone().ok_or(NoSuperFound)?.with_this(388 ctx.this()389 .clone()390 .expect("if super exists - then this should to"),391 ),392 ),393 Literal(LiteralType::Dollar) => {394 Val::Obj(ctx.dollar().clone().ok_or(NoTopLevelObjectFound)?)395 }396 Literal(LiteralType::True) => Val::Bool(true),397 Literal(LiteralType::False) => Val::Bool(false),398 Literal(LiteralType::Null) => Val::Null,399 Parened(e) => evaluate(ctx, e)?,400 Str(v) => Val::Str(v.clone()),401 Num(v) => Val::new_checked_num(*v)?,402 BinaryOp(v1, o, v2) => evaluate_binary_op_special(ctx, v1, *o, v2)?,403 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(ctx, v)?)?,404 Var(name) => State::push(405 CallLocation::new(loc),406 || format!("variable <{name}> access"),407 || ctx.binding(name.clone())?.evaluate(),408 )?,409 Index(value, index) => match (evaluate(ctx.clone(), value)?, evaluate(ctx, index)?) {410 (Val::Obj(v), Val::Str(key)) => State::push(411 CallLocation::new(loc),412 || format!("field <{key}> access"),413 || match v.get(key.clone()) {414 Ok(Some(v)) => Ok(v),415 #[cfg(not(feature = "friendly-errors"))]416 Ok(None) => throw!(NoSuchField(key.clone(), vec![])),417 #[cfg(feature = "friendly-errors")]418 Ok(None) => {419 let mut heap = Vec::new();420 for field in v.fields_ex(421 true,422 #[cfg(feature = "exp-preserve-order")]423 false,424 ) {425 let conf = strsim::jaro_winkler(&field as &str, &key as &str);426 if conf < 0.8 {427 continue;428 }429 heap.push((conf, field));430 }431 heap.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));432433 throw!(NoSuchField(434 key.clone(),435 heap.into_iter().map(|(_, v)| v).collect()436 ))437 }438 Err(e) => Err(e),439 },440 )?,441 (Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot(442 ValType::Obj,443 ValType::Str,444 n.value_type(),445 )),446447 (Val::Arr(v), Val::Num(n)) => {448 if n.fract() > f64::EPSILON {449 throw!(FractionalIndex)450 }451 v.get(n as usize)?452 .ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?453 }454 (Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),455 (Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot(456 ValType::Arr,457 ValType::Num,458 n.value_type(),459 )),460461 (Val::Str(s), Val::Num(n)) => Val::Str({462 let v: IStr = s463 .chars()464 .skip(n as usize)465 .take(1)466 .collect::<String>()467 .into();468 if v.is_empty() {469 let size = s.chars().count();470 throw!(StringBoundsError(n as usize, size))471 }472 v473 }),474 (Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot(475 ValType::Str,476 ValType::Num,477 n.value_type(),478 )),479480 (v, _) => throw!(CantIndexInto(v.value_type())),481 },482 LocalExpr(bindings, returned) => {483 let mut new_bindings: GcHashMap<IStr, Thunk<Val>> =484 GcHashMap::with_capacity(bindings.len());485 let fctx = Context::new_future();486 for b in bindings {487 evaluate_dest(b, fctx.clone(), &mut new_bindings)?;488 }489 let ctx = ctx.extend(new_bindings, None, None, None).into_future(fctx);490 evaluate(ctx, &returned.clone())?491 }492 Arr(items) => {493 let mut out = Vec::with_capacity(items.len());494 for item in items {495 // TODO: Implement ArrValue::Lazy with same context for every element?496 #[derive(Trace)]497 struct ArrayElement {498 ctx: Context,499 item: LocExpr,500 }501 impl ThunkValue for ArrayElement {502 type Output = Val;503 fn get(self: Box<Self>) -> Result<Val> {504 evaluate(self.ctx, &self.item)505 }506 }507 out.push(Thunk::new(tb!(ArrayElement {508 ctx: ctx.clone(),509 item: item.clone(),510 })));511 }512 Val::Arr(out.into())513 }514 ArrComp(expr, comp_specs) => {515 let mut out = Vec::new();516 evaluate_comp(ctx, comp_specs, &mut |ctx| {517 out.push(evaluate(ctx, expr)?);518 Ok(())519 })?;520 Val::Arr(ArrValue::Eager(Cc::new(out)))521 }522 Obj(body) => Val::Obj(evaluate_object(ctx, body)?),523 ObjExtend(a, b) => evaluate_add_op(524 &evaluate(ctx.clone(), a)?,525 &Val::Obj(evaluate_object(ctx, b)?),526 )?,527 Apply(value, args, tailstrict) => {528 evaluate_apply(ctx, value, args, CallLocation::new(loc), *tailstrict)?529 }530 Function(params, body) => {531 evaluate_method(ctx, "anonymous".into(), params.clone(), body.clone())532 }533 AssertExpr(assert, returned) => {534 evaluate_assert(ctx.clone(), assert)?;535 evaluate(ctx, returned)?536 }537 ErrorStmt(e) => State::push(538 CallLocation::new(loc),539 || "error statement".to_owned(),540 || throw!(RuntimeError(evaluate(ctx, e)?.to_string()?,)),541 )?,542 IfElse {543 cond,544 cond_then,545 cond_else,546 } => {547 if State::push(548 CallLocation::new(loc),549 || "if condition".to_owned(),550 || bool::from_untyped(evaluate(ctx.clone(), &cond.0)?),551 )? {552 evaluate(ctx, cond_then)?553 } else {554 match cond_else {555 Some(v) => evaluate(ctx, v)?,556 None => Val::Null,557 }558 }559 }560 Slice(value, desc) => {561 fn parse_idx<T: Typed>(562 loc: CallLocation<'_>,563 ctx: &Context,564 expr: &Option<LocExpr>,565 desc: &'static str,566 ) -> Result<Option<T>> {567 if let Some(value) = expr {568 Ok(Some(State::push(569 loc,570 || format!("slice {desc}"),571 || T::from_untyped(evaluate(ctx.clone(), value)?),572 )?))573 } else {574 Ok(None)575 }576 }577578 let indexable = evaluate(ctx.clone(), value)?;579 let loc = CallLocation::new(loc);580581 let start = parse_idx(loc, &ctx, &desc.start, "start")?;582 let end = parse_idx(loc, &ctx, &desc.end, "end")?;583 let step = parse_idx(loc, &ctx, &desc.step, "step")?;584585 IndexableVal::into_untyped(indexable.into_indexable()?.slice(start, end, step)?)?586 }587 i @ (Import(path) | ImportStr(path) | ImportBin(path)) => {588 let tmp = loc.clone().0;589 let s = ctx.state();590 let resolved_path = s.resolve_from(tmp.source_path(), path as &str)?;591 match i {592 Import(_) => State::push(593 CallLocation::new(loc),594 || format!("import {:?}", path.clone()),595 || s.import_resolved(resolved_path),596 )?,597 ImportStr(_) => Val::Str(s.import_resolved_str(resolved_path)?),598 ImportBin(_) => Val::Arr(ArrValue::Bytes(s.import_resolved_bin(resolved_path)?)),599 _ => unreachable!(),600 }601 }602 })603}1use std::{cmp::Ordering, rc::Rc};23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::IStr;5use jrsonnet_parser::{6 ArgsDesc, AssertStmt, BindSpec, CompSpec, Expr, FieldMember, FieldName, ForSpecData,7 IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc,8};9use jrsonnet_types::ValType;1011use crate::{12 destructure::evaluate_dest,13 error::Error::*,14 evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},15 function::{CallLocation, FuncDesc, FuncVal},16 tb, throw,17 typed::Typed,18 val::{ArrValue, CachedUnbound, IndexableVal, Thunk, ThunkValue},19 Context, GcHashMap, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result, State,20 Unbound, Val,21};22pub mod destructure;23pub mod operator;2425pub fn evaluate_method(ctx: Context, name: IStr, params: ParamsDesc, body: LocExpr) -> Val {26 Val::Func(FuncVal::Normal(Cc::new(FuncDesc {27 name,28 ctx,29 params,30 body,31 })))32}3334pub fn evaluate_field_name(ctx: Context, field_name: &FieldName) -> Result<Option<IStr>> {35 Ok(match field_name {36 FieldName::Fixed(n) => Some(n.clone()),37 FieldName::Dyn(expr) => State::push(38 CallLocation::new(&expr.1),39 || "evaluating field name".to_string(),40 || {41 let value = evaluate(ctx, expr)?;42 if matches!(value, Val::Null) {43 Ok(None)44 } else {45 Ok(Some(IStr::from_untyped(value)?))46 }47 },48 )?,49 })50}5152pub fn evaluate_comp(53 ctx: Context,54 specs: &[CompSpec],55 callback: &mut impl FnMut(Context) -> Result<()>,56) -> Result<()> {57 match specs.get(0) {58 None => callback(ctx)?,59 Some(CompSpec::IfSpec(IfSpecData(cond))) => {60 if bool::from_untyped(evaluate(ctx.clone(), cond)?)? {61 evaluate_comp(ctx, &specs[1..], callback)?;62 }63 }64 Some(CompSpec::ForSpec(ForSpecData(var, expr))) => match evaluate(ctx.clone(), expr)? {65 Val::Arr(list) => {66 for item in list.iter() {67 evaluate_comp(68 ctx.clone().with_var(var.clone(), item?.clone()),69 &specs[1..],70 callback,71 )?;72 }73 }74 _ => throw!(InComprehensionCanOnlyIterateOverArray),75 },76 }77 Ok(())78}7980trait CloneableUnbound<T>: Unbound<Bound = T> + Clone {}8182fn evaluate_object_locals(83 fctx: Pending<Context>,84 locals: Rc<Vec<BindSpec>>,85) -> impl CloneableUnbound<Context> {86 #[derive(Trace, Clone)]87 struct UnboundLocals {88 fctx: Pending<Context>,89 locals: Rc<Vec<BindSpec>>,90 }91 impl CloneableUnbound<Context> for UnboundLocals {}92 impl Unbound for UnboundLocals {93 type Bound = Context;9495 fn bind(&self, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<Context> {96 let fctx = Context::new_future();97 let mut new_bindings = GcHashMap::new();98 for b in self.locals.iter() {99 evaluate_dest(b, fctx.clone(), &mut new_bindings)?;100 }101102 let ctx = self.fctx.unwrap();103 let new_dollar = ctx.dollar().clone().or_else(|| this.clone());104105 let ctx = ctx106 .extend(new_bindings, new_dollar, sup, this)107 .into_future(fctx);108109 Ok(ctx)110 }111 }112113 UnboundLocals { fctx, locals }114}115116#[allow(clippy::too_many_lines)]117pub fn evaluate_member_list_object(ctx: Context, members: &[Member]) -> Result<ObjValue> {118 let mut builder = ObjValueBuilder::new();119 let locals = Rc::new(120 members121 .iter()122 .filter_map(|m| match m {123 Member::BindStmt(bind) => Some(bind.clone()),124 _ => None,125 })126 .collect::<Vec<_>>(),127 );128129 let fctx = Context::new_future();130131 // We have single context for all fields, so we can cache binds132 let uctx = CachedUnbound::new(evaluate_object_locals(fctx.clone(), locals));133134 for member in members.iter() {135 match member {136 Member::Field(FieldMember {137 name,138 plus,139 params: None,140 visibility,141 value,142 }) => {143 #[derive(Trace)]144 struct UnboundValue<B: Trace> {145 uctx: B,146 value: LocExpr,147 name: IStr,148 }149 impl<B: Unbound<Bound = Context>> Unbound for UnboundValue<B> {150 type Bound = Thunk<Val>;151 fn bind(152 &self,153 sup: Option<ObjValue>,154 this: Option<ObjValue>,155 ) -> Result<Thunk<Val>> {156 Ok(Thunk::evaluated(evaluate_named(157 self.uctx.bind(sup, this)?,158 &self.value,159 self.name.clone(),160 )?))161 }162 }163164 let name = evaluate_field_name(ctx.clone(), name)?;165 let Some(name) = name else {166 continue;167 };168169 builder170 .member(name.clone())171 .with_add(*plus)172 .with_visibility(*visibility)173 .with_location(value.1.clone())174 .bindable(tb!(UnboundValue {175 uctx: uctx.clone(),176 value: value.clone(),177 name: name.clone()178 }))?;179 }180 Member::Field(FieldMember {181 name,182 params: Some(params),183 value,184 ..185 }) => {186 #[derive(Trace)]187 struct UnboundMethod<B: Trace> {188 uctx: B,189 value: LocExpr,190 params: ParamsDesc,191 name: IStr,192 }193 impl<B: Unbound<Bound = Context>> Unbound for UnboundMethod<B> {194 type Bound = Thunk<Val>;195 fn bind(196 &self,197 sup: Option<ObjValue>,198 this: Option<ObjValue>,199 ) -> Result<Thunk<Val>> {200 Ok(Thunk::evaluated(evaluate_method(201 self.uctx.bind(sup, this)?,202 self.name.clone(),203 self.params.clone(),204 self.value.clone(),205 )))206 }207 }208209 let name = if let Some(name) = evaluate_field_name(ctx.clone(), name)? {210 name211 } else {212 continue;213 };214215 builder216 .member(name.clone())217 .hide()218 .with_location(value.1.clone())219 .bindable(tb!(UnboundMethod {220 uctx: uctx.clone(),221 value: value.clone(),222 params: params.clone(),223 name: name.clone()224 }))?;225 }226 Member::BindStmt(_) => {}227 Member::AssertStmt(stmt) => {228 #[derive(Trace)]229 struct ObjectAssert<B: Trace> {230 uctx: B,231 assert: AssertStmt,232 }233 impl<B: Unbound<Bound = Context>> ObjectAssertion for ObjectAssert<B> {234 fn run(&self, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<()> {235 let ctx = self.uctx.bind(sup, this)?;236 evaluate_assert(ctx, &self.assert)237 }238 }239 builder.assert(tb!(ObjectAssert {240 uctx: uctx.clone(),241 assert: stmt.clone(),242 }));243 }244 }245 }246 let this = builder.build();247 fctx.fill(ctx.extend(GcHashMap::new(), None, None, Some(this.clone())));248 Ok(this)249}250251pub fn evaluate_object(ctx: Context, object: &ObjBody) -> Result<ObjValue> {252 Ok(match object {253 ObjBody::MemberList(members) => evaluate_member_list_object(ctx, members)?,254 ObjBody::ObjComp(obj) => {255 let mut builder = ObjValueBuilder::new();256 let locals = Rc::new(257 obj.pre_locals258 .iter()259 .chain(obj.post_locals.iter())260 .cloned()261 .collect::<Vec<_>>(),262 );263 let mut ctxs = vec![];264 evaluate_comp(ctx, &obj.compspecs, &mut |ctx| {265 let key = evaluate(ctx.clone(), &obj.key)?;266 let fctx = Context::new_future();267 ctxs.push((ctx, fctx.clone()));268 let uctx = evaluate_object_locals(fctx, locals.clone());269270 match key {271 Val::Null => {}272 Val::Str(n) => {273 #[derive(Trace)]274 struct UnboundValue<B: Trace> {275 uctx: B,276 value: LocExpr,277 }278 impl<B: Unbound<Bound = Context>> Unbound for UnboundValue<B> {279 type Bound = Thunk<Val>;280 fn bind(281 &self,282 sup: Option<ObjValue>,283 this: Option<ObjValue>,284 ) -> Result<Thunk<Val>> {285 Ok(Thunk::evaluated(evaluate(286 self.uctx.bind(sup, this.clone())?.extend(287 GcHashMap::new(),288 None,289 None,290 this,291 ),292 &self.value,293 )?))294 }295 }296 builder297 .member(n)298 .with_location(obj.value.1.clone())299 .with_add(obj.plus)300 .bindable(tb!(UnboundValue {301 uctx,302 value: obj.value.clone(),303 }))?;304 }305 v => throw!(FieldMustBeStringGot(v.value_type())),306 }307308 Ok(())309 })?;310311 let this = builder.build();312 for (ctx, fctx) in ctxs {313 let _ctx = ctx314 .extend(GcHashMap::new(), None, None, Some(this.clone()))315 .into_future(fctx);316 }317 this318 }319 })320}321322pub fn evaluate_apply(323 ctx: Context,324 value: &LocExpr,325 args: &ArgsDesc,326 loc: CallLocation<'_>,327 tailstrict: bool,328) -> Result<Val> {329 let value = evaluate(ctx.clone(), value)?;330 Ok(match value {331 Val::Func(f) => {332 let body = || f.evaluate(ctx, loc, args, tailstrict);333 if tailstrict {334 body()?335 } else {336 State::push(loc, || format!("function <{}> call", f.name()), body)?337 }338 }339 v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type())),340 })341}342343pub fn evaluate_assert(ctx: Context, assertion: &AssertStmt) -> Result<()> {344 let value = &assertion.0;345 let msg = &assertion.1;346 let assertion_result = State::push(347 CallLocation::new(&value.1),348 || "assertion condition".to_owned(),349 || bool::from_untyped(evaluate(ctx.clone(), value)?),350 )?;351 if !assertion_result {352 State::push(353 CallLocation::new(&value.1),354 || "assertion failure".to_owned(),355 || {356 if let Some(msg) = msg {357 throw!(AssertionFailed(evaluate(ctx, msg)?.to_string()?));358 }359 throw!(AssertionFailed(Val::Null.to_string()?));360 },361 )?;362 }363 Ok(())364}365366pub fn evaluate_named(ctx: Context, expr: &LocExpr, name: IStr) -> Result<Val> {367 use Expr::*;368 let LocExpr(raw_expr, _loc) = expr;369 Ok(match &**raw_expr {370 Function(params, body) => evaluate_method(ctx, name, params.clone(), body.clone()),371 _ => evaluate(ctx, expr)?,372 })373}374375#[allow(clippy::too_many_lines)]376pub fn evaluate(ctx: Context, expr: &LocExpr) -> Result<Val> {377 use Expr::*;378 let LocExpr(expr, loc) = expr;379 // let bp = with_state(|s| s.0.stop_at.borrow().clone());380 Ok(match &**expr {381 Literal(LiteralType::This) => {382 Val::Obj(ctx.this().clone().ok_or(CantUseSelfOutsideOfObject)?)383 }384 Literal(LiteralType::Super) => Val::Obj(385 ctx.super_obj().clone().ok_or(NoSuperFound)?.with_this(386 ctx.this()387 .clone()388 .expect("if super exists - then this should to"),389 ),390 ),391 Literal(LiteralType::Dollar) => {392 Val::Obj(ctx.dollar().clone().ok_or(NoTopLevelObjectFound)?)393 }394 Literal(LiteralType::True) => Val::Bool(true),395 Literal(LiteralType::False) => Val::Bool(false),396 Literal(LiteralType::Null) => Val::Null,397 Parened(e) => evaluate(ctx, e)?,398 Str(v) => Val::Str(v.clone()),399 Num(v) => Val::new_checked_num(*v)?,400 BinaryOp(v1, o, v2) => evaluate_binary_op_special(ctx, v1, *o, v2)?,401 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(ctx, v)?)?,402 Var(name) => State::push(403 CallLocation::new(loc),404 || format!("variable <{name}> access"),405 || ctx.binding(name.clone())?.evaluate(),406 )?,407 Index(value, index) => match (evaluate(ctx.clone(), value)?, evaluate(ctx, index)?) {408 (Val::Obj(v), Val::Str(key)) => State::push(409 CallLocation::new(loc),410 || format!("field <{key}> access"),411 || match v.get(key.clone()) {412 Ok(Some(v)) => Ok(v),413 #[cfg(not(feature = "friendly-errors"))]414 Ok(None) => throw!(NoSuchField(key.clone(), vec![])),415 #[cfg(feature = "friendly-errors")]416 Ok(None) => {417 let mut heap = Vec::new();418 for field in v.fields_ex(419 true,420 #[cfg(feature = "exp-preserve-order")]421 false,422 ) {423 let conf = strsim::jaro_winkler(&field as &str, &key as &str);424 if conf < 0.8 {425 continue;426 }427 heap.push((conf, field));428 }429 heap.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));430431 throw!(NoSuchField(432 key.clone(),433 heap.into_iter().map(|(_, v)| v).collect()434 ))435 }436 Err(e) => Err(e),437 },438 )?,439 (Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot(440 ValType::Obj,441 ValType::Str,442 n.value_type(),443 )),444445 (Val::Arr(v), Val::Num(n)) => {446 if n.fract() > f64::EPSILON {447 throw!(FractionalIndex)448 }449 v.get(n as usize)?450 .ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?451 }452 (Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),453 (Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot(454 ValType::Arr,455 ValType::Num,456 n.value_type(),457 )),458459 (Val::Str(s), Val::Num(n)) => Val::Str({460 let v: IStr = s461 .chars()462 .skip(n as usize)463 .take(1)464 .collect::<String>()465 .into();466 if v.is_empty() {467 let size = s.chars().count();468 throw!(StringBoundsError(n as usize, size))469 }470 v471 }),472 (Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot(473 ValType::Str,474 ValType::Num,475 n.value_type(),476 )),477478 (v, _) => throw!(CantIndexInto(v.value_type())),479 },480 LocalExpr(bindings, returned) => {481 let mut new_bindings: GcHashMap<IStr, Thunk<Val>> =482 GcHashMap::with_capacity(bindings.len());483 let fctx = Context::new_future();484 for b in bindings {485 evaluate_dest(b, fctx.clone(), &mut new_bindings)?;486 }487 let ctx = ctx.extend(new_bindings, None, None, None).into_future(fctx);488 evaluate(ctx, &returned.clone())?489 }490 Arr(items) => {491 let mut out = Vec::with_capacity(items.len());492 for item in items {493 // TODO: Implement ArrValue::Lazy with same context for every element?494 #[derive(Trace)]495 struct ArrayElement {496 ctx: Context,497 item: LocExpr,498 }499 impl ThunkValue for ArrayElement {500 type Output = Val;501 fn get(self: Box<Self>) -> Result<Val> {502 evaluate(self.ctx, &self.item)503 }504 }505 out.push(Thunk::new(tb!(ArrayElement {506 ctx: ctx.clone(),507 item: item.clone(),508 })));509 }510 Val::Arr(out.into())511 }512 ArrComp(expr, comp_specs) => {513 let mut out = Vec::new();514 evaluate_comp(ctx, comp_specs, &mut |ctx| {515 out.push(evaluate(ctx, expr)?);516 Ok(())517 })?;518 Val::Arr(ArrValue::Eager(Cc::new(out)))519 }520 Obj(body) => Val::Obj(evaluate_object(ctx, body)?),521 ObjExtend(a, b) => evaluate_add_op(522 &evaluate(ctx.clone(), a)?,523 &Val::Obj(evaluate_object(ctx, b)?),524 )?,525 Apply(value, args, tailstrict) => {526 evaluate_apply(ctx, value, args, CallLocation::new(loc), *tailstrict)?527 }528 Function(params, body) => {529 evaluate_method(ctx, "anonymous".into(), params.clone(), body.clone())530 }531 AssertExpr(assert, returned) => {532 evaluate_assert(ctx.clone(), assert)?;533 evaluate(ctx, returned)?534 }535 ErrorStmt(e) => State::push(536 CallLocation::new(loc),537 || "error statement".to_owned(),538 || throw!(RuntimeError(evaluate(ctx, e)?.to_string()?,)),539 )?,540 IfElse {541 cond,542 cond_then,543 cond_else,544 } => {545 if State::push(546 CallLocation::new(loc),547 || "if condition".to_owned(),548 || bool::from_untyped(evaluate(ctx.clone(), &cond.0)?),549 )? {550 evaluate(ctx, cond_then)?551 } else {552 match cond_else {553 Some(v) => evaluate(ctx, v)?,554 None => Val::Null,555 }556 }557 }558 Slice(value, desc) => {559 fn parse_idx<T: Typed>(560 loc: CallLocation<'_>,561 ctx: &Context,562 expr: &Option<LocExpr>,563 desc: &'static str,564 ) -> Result<Option<T>> {565 if let Some(value) = expr {566 Ok(Some(State::push(567 loc,568 || format!("slice {desc}"),569 || T::from_untyped(evaluate(ctx.clone(), value)?),570 )?))571 } else {572 Ok(None)573 }574 }575576 let indexable = evaluate(ctx.clone(), value)?;577 let loc = CallLocation::new(loc);578579 let start = parse_idx(loc, &ctx, &desc.start, "start")?;580 let end = parse_idx(loc, &ctx, &desc.end, "end")?;581 let step = parse_idx(loc, &ctx, &desc.step, "step")?;582583 IndexableVal::into_untyped(indexable.into_indexable()?.slice(start, end, step)?)?584 }585 i @ (Import(path) | ImportStr(path) | ImportBin(path)) => {586 let tmp = loc.clone().0;587 let s = ctx.state();588 let resolved_path = s.resolve_from(tmp.source_path(), path as &str)?;589 match i {590 Import(_) => State::push(591 CallLocation::new(loc),592 || format!("import {:?}", path.clone()),593 || s.import_resolved(resolved_path),594 )?,595 ImportStr(_) => Val::Str(s.import_resolved_str(resolved_path)?),596 ImportBin(_) => Val::Arr(ArrValue::Bytes(s.import_resolved_bin(resolved_path)?)),597 _ => unreachable!(),598 }599 }600 })601}crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -344,10 +344,10 @@
let mut file_cache = self.file_cache();
let mut file = file_cache.raw_entry_mut().from_key(&path);
- let file = match file {
- RawEntryMut::Occupied(ref mut d) => d.get_mut(),
- RawEntryMut::Vacant(_) => unreachable!("this file was just here!"),
+ let RawEntryMut::Occupied(file) = &mut file else {
+ unreachable!("this file was just here!")
};
+ let file = file.get_mut();
file.evaluating = false;
match res {
Ok(v) => {
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -54,12 +54,8 @@
ThunkInner::Pending => return Err(InfiniteRecursionDetected.into()),
ThunkInner::Waiting(..) => (),
};
- let value = if let ThunkInner::Waiting(value) =
- std::mem::replace(&mut *self.0.borrow_mut(), ThunkInner::Pending)
- {
- value
- } else {
- unreachable!()
+ let ThunkInner::Waiting(value) = std::mem::replace(&mut *self.0.borrow_mut(), ThunkInner::Pending) else {
+ unreachable!();
};
let new_value = match value.0.get() {
Ok(v) => v,
@@ -668,9 +664,8 @@
/// Expects value to be object, outputs (key, manifested value) pairs
pub fn manifest_multi(&self, ty: &ManifestFormat) -> Result<Vec<(IStr, IStr)>> {
- let obj = match self {
- Self::Obj(obj) => obj,
- _ => throw!(MultiManifestOutputIsNotAObject),
+ let Self::Obj(obj) = self else {
+ throw!(MultiManifestOutputIsNotAObject);
};
let keys = obj.fields(
#[cfg(feature = "exp-preserve-order")]
@@ -689,9 +684,8 @@
/// Expects value to be array, outputs manifested values
pub fn manifest_stream(&self, ty: &ManifestFormat) -> Result<Vec<IStr>> {
- let arr = match self {
- Self::Arr(a) => a,
- _ => throw!(StreamManifestOutputIsNotAArray),
+ let Self::Arr(arr) = self else {
+ throw!(StreamManifestOutputIsNotAArray);
};
let mut out = Vec::with_capacity(arr.len());
for i in arr.iter() {
@@ -703,9 +697,8 @@
pub fn manifest(&self, ty: &ManifestFormat) -> Result<IStr> {
Ok(match ty {
ManifestFormat::YamlStream(format) => {
- let arr = match self {
- Self::Arr(a) => a,
- _ => throw!(StreamManifestOutputIsNotAArray),
+ let Self::Arr(arr) = self else {
+ throw!(StreamManifestOutputIsNotAArray)
};
let mut out = String::new();
crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-macros/src/lib.rs
+++ b/crates/jrsonnet-macros/src/lib.rs
@@ -50,25 +50,22 @@
}
fn extract_type_from_option(ty: &Type) -> Result<Option<&Type>> {
- Ok(if let Some(args) = type_is_path(ty, "Option") {
- // It should have only on angle-bracketed param ("<String>"):
- let generic_arg = match args {
- PathArguments::AngleBracketed(params) => params.args.iter().next().unwrap(),
- _ => return Err(Error::new(args.span(), "missing option generic")),
- };
- // This argument must be a type:
- match generic_arg {
- GenericArgument::Type(ty) => Some(ty),
- _ => {
- return Err(Error::new(
- generic_arg.span(),
- "option generic should be a type",
- ))
- }
- }
- } else {
- None
- })
+ let Some(args) = type_is_path(ty, "Option") else {
+ return Ok(None)
+ };
+ // It should have only on angle-bracketed param ("<String>"):
+ let PathArguments::AngleBracketed(params) = args else {
+ return Err(Error::new(args.span(), "missing option generic"));
+ };
+ let generic_arg = params.args.iter().next().unwrap();
+ // This argument must be a type:
+ let GenericArgument::Type(ty) = generic_arg else {
+ return Err(Error::new(
+ generic_arg.span(),
+ "option generic should be a type",
+ ))
+ };
+ Ok(Some(ty))
}
struct Field {
@@ -137,9 +134,8 @@
impl ArgInfo {
fn parse(name: &str, arg: &FnArg) -> Result<Self> {
- let arg = match arg {
- FnArg::Receiver(_) => unreachable!(),
- FnArg::Typed(a) => a,
+ let FnArg::Typed(arg) = arg else {
+ unreachable!()
};
let ident = match &arg.pat as &Pat {
Pat::Ident(i) => Some(i.ident.clone()),
@@ -206,33 +202,28 @@
}
fn builtin_inner(attr: BuiltinAttrs, fun: ItemFn) -> syn::Result<TokenStream> {
- let result = match fun.sig.output {
- ReturnType::Default => {
- return Err(Error::new(
- fun.sig.span(),
- "builtin should return something",
- ))
- }
- ReturnType::Type(_, ref ty) => ty.clone(),
+ let ReturnType::Type(_, result) = &fun.sig.output else {
+ return Err(Error::new(
+ fun.sig.span(),
+ "builtin should return something",
+ ))
};
- let result_inner = if let Some(args) = type_is_path(&result, "Result") {
- let generic_arg = match args {
- PathArguments::AngleBracketed(params) => params.args.iter().next().unwrap(),
- _ => return Err(Error::new(args.span(), "missing result generic")),
- };
- // This argument must be a type:
- match generic_arg {
- GenericArgument::Type(ty) => ty,
- _ => {
- return Err(Error::new(
- generic_arg.span(),
- "option generic should be a type",
- ))
- }
- }
- } else {
+
+ let Some(args) = type_is_path(result, "Result") else {
return Err(Error::new(result.span(), "return value should be result"));
+
+ };
+ let PathArguments::AngleBracketed(params) = args else {
+ return Err(Error::new(args.span(), "missing result generic"));
};
+ let generic_arg = params.args.iter().next().unwrap();
+ // This argument must be a type:
+ let GenericArgument::Type(result_inner) = generic_arg else {
+ return Err(Error::new(
+ generic_arg.span(),
+ "option generic should be a type",
+ ))
+ };
let name = fun.sig.ident.to_string();
let args = fun
@@ -471,9 +462,7 @@
impl TypedField {
fn parse(field: &syn::Field) -> Result<Self> {
let attr = parse_attr::<TypedAttr, _>(&field.attrs, "typed")?.unwrap_or_default();
- let ident = if let Some(ident) = field.ident.clone() {
- ident
- } else {
+ let Some(ident) = field.ident.clone() else {
return Err(Error::new(
field.span(),
"this field should appear in output object, but it has no visible name",
@@ -603,9 +592,8 @@
}
fn derive_typed_inner(input: DeriveInput) -> Result<TokenStream> {
- let data = match &input.data {
- syn::Data::Struct(s) => s,
- _ => return Err(Error::new(input.span(), "only structs supported")),
+ let syn::Data::Struct(data) = &input.data else {
+ return Err(Error::new(input.span(), "only structs supported"));
};
let ident = &input.ident;
crates/jrsonnet-parser/src/source.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/source.rs
+++ b/crates/jrsonnet-parser/src/source.rs
@@ -32,10 +32,8 @@
self.hash(&mut hasher)
}
fn dyn_eq(&self, other: &dyn $T) -> bool {
- let other = if let Some(v) = other.as_any().downcast_ref::<Self>() {
- v
- } else {
- return false;
+ let Some(other) = other.as_any().downcast_ref::<Self>() else {
+ return false
};
let this = <Self as $T>::as_any(self)
.downcast_ref::<Self>()
tests/tests/sanity.rsdiffbeforeafterboth--- a/tests/tests/sanity.rs
+++ b/tests/tests/sanity.rs
@@ -22,17 +22,15 @@
s.with_stdlib();
{
- let e = match s.evaluate_snippet("snip".to_owned(), "assert 1 == 2: 'fail'; null") {
- Ok(_) => throw!("assertion should fail"),
- Err(e) => e,
+ let Err(e) = s.evaluate_snippet("snip".to_owned(), "assert 1 == 2: 'fail'; null") else {
+ throw!("assertion should fail");
};
let e = s.stringify_err(&e);
ensure!(e.starts_with("assert failed: fail\n"));
}
{
- let e = match s.evaluate_snippet("snip".to_owned(), "std.assertEqual(1, 2)") {
- Ok(_) => throw!("assertion should fail"),
- Err(e) => e,
+ let Err(e) = s.evaluate_snippet("snip".to_owned(), "std.assertEqual(1, 2)") else {
+ throw!("assertion should fail")
};
let e = s.stringify_err(&e);
ensure!(e.starts_with("runtime error: Assertion failed. 1 != 2"))