From df9bc99da41f8794b18db4cdd395d2fb047d2bcb Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sat, 27 Nov 2021 20:07:57 +0000 Subject: [PATCH] feat: std.parseYaml intrinsic --- --- a/crates/jrsonnet-evaluator/Cargo.toml +++ b/crates/jrsonnet-evaluator/Cargo.toml @@ -7,11 +7,9 @@ edition = "2018" [features] -default = ["serialized-stdlib", "explaining-traces", "serde-json"] +default = ["serialized-stdlib", "explaining-traces"] # Serializes standard library AST instead of parsing them every run -serialized-stdlib = ["serde", "bincode", "jrsonnet-parser/deserialize"] -# Allow to convert Val into serde_json::Value and backwards -serde-json = ["serde", "serde_json"] +serialized-stdlib = ["bincode", "jrsonnet-parser/deserialize"] # Rustc-like trace visualization explaining-traces = ["annotate-snippets"] # Allows library authors to throw custom errors @@ -34,21 +32,17 @@ thiserror = "1.0" gcmodule = { git = "https://github.com/CertainLach/gcmodule", branch = "jrsonnet" } +serde = "1.0" +serde_json = "1.0" +serde_yaml = { git = "https://github.com/CertainLach/serde-yaml", branch = "feature/old-octals-quirk" } + [dependencies.anyhow] version = "1.0" optional = true # Serialized stdlib -[dependencies.serde] -version = "1.0" -optional = true [dependencies.bincode] version = "1.3.1" -optional = true - -# Serde json -[dependencies.serde_json] -version = "1.0" optional = true # Explaining traces --- a/crates/jrsonnet-evaluator/src/builtin/mod.rs +++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs @@ -3,15 +3,17 @@ equals, error::{Error::*, Result}, operator::evaluate_mod_op, - parse_args, primitive_equals, push_frame, throw, with_state, ArrValue, Context, - EvaluationState, FuncVal, IndexableVal, LazyVal, Val, + parse_args, primitive_equals, push_frame, throw, with_state, ArrValue, Context, FuncVal, + IndexableVal, LazyVal, Val, }; use format::{format_arr, format_obj}; use gcmodule::Cc; use jrsonnet_interner::IStr; use jrsonnet_parser::{ArgsDesc, ExprLocation}; use jrsonnet_types::ty; -use std::{collections::HashMap, path::PathBuf, rc::Rc}; +use serde::Deserialize; +use serde_yaml::DeserializingQuirks; +use std::{collections::HashMap, convert::TryFrom, path::PathBuf, rc::Rc}; pub mod stdlib; pub use stdlib::*; @@ -128,6 +130,7 @@ ("strReplace".into(), builtin_str_replace), ("splitLimit".into(), builtin_splitlimit), ("parseJson".into(), builtin_parse_json), + ("parseYaml".into(), builtin_parse_yaml), ("asciiUpper".into(), builtin_ascii_upper), ("asciiLower".into(), builtin_ascii_lower), ("member".into(), builtin_member), @@ -210,9 +213,30 @@ parse_args!(context, "parseJson", args, 1, [ 0, s: ty!(string) => Val::Str; ], { - let state = EvaluationState::default(); - let path = PathBuf::from("std.parseJson").into(); - state.evaluate_snippet_raw(path ,s) + let value: serde_json::Value = serde_json::from_str(&s).map_err(|e| RuntimeError(format!("failed to parse json: {}", e).into()))?; + Ok(Val::try_from(&value)?) + }) +} + +fn builtin_parse_yaml(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result { + parse_args!(context, "parseYaml", args, 1, [ + 0, s: ty!(string) => Val::Str; + ], { + let value = serde_yaml::Deserializer::from_str_with_quirks(&s, DeserializingQuirks { old_octals: true }); + let mut out = vec![]; + for item in value { + let value = serde_json::Value::deserialize(item) + .map_err(|e| RuntimeError(format!("failed to parse yaml: {}", e).into()))?; + let val = Val::try_from(&value)?; + out.push(val); + } + if out.is_empty() { + Ok(Val::Null) + } else if out.len() == 1 { + Ok(out.into_iter().next().unwrap()) + } else { + Ok(Val::Arr(out.into())) + } }) } --- a/crates/jrsonnet-evaluator/src/integrations/mod.rs +++ b/crates/jrsonnet-evaluator/src/integrations/mod.rs @@ -1,2 +1 @@ -#[cfg(feature = "serde-json")] pub mod serde; --- a/crates/jrsonnet-evaluator/src/integrations/serde.rs +++ b/crates/jrsonnet-evaluator/src/integrations/serde.rs @@ -15,7 +15,7 @@ Val::Num(n) => Self::Number(if n.fract() <= f64::EPSILON { (*n as i64).into() } else { - Number::from_f64(*n).expect("to json number") + Number::from_f64(*n).expect("jsonnet numbers can't be infinite or NaN") }), Val::Arr(a) => { let mut out = Vec::with_capacity(a.len()); @@ -29,7 +29,9 @@ for key in o.fields() { out.insert( (&key as &str).into(), - (&o.get(key)?.expect("field exists")).try_into()?, + (&o.get(key)? + .expect("key is present in fields, so value should exist")) + .try_into()?, ); } Self::Object(out) @@ -39,27 +41,30 @@ } } -impl From<&Value> for Val { - fn from(v: &Value) -> Self { - match v { +impl TryFrom<&Value> for Val { + type Error = LocError; + fn try_from(v: &Value) -> Result { + Ok(match v { Value::Null => Self::Null, Value::Bool(v) => Self::Bool(*v), - Value::Number(n) => Self::Num(n.as_f64().expect("as f64")), + Value::Number(n) => Self::Num(n.as_f64().ok_or_else(|| { + RuntimeError(format!("json number can't be represented as jsonnet: {}", n).into()) + })?), Value::String(s) => Self::Str((s as &str).into()), Value::Array(a) => { let mut out: Vec = Vec::with_capacity(a.len()); for v in a { - out.push(v.into()); + out.push(v.try_into()?); } Self::Arr(out.into()) } Value::Object(o) => { let mut builder = ObjValueBuilder::with_capacity(o.len()); for (k, v) in o { - builder.member((k as &str).into()).value(v.into()); + builder.member((k as &str).into()).value(v.try_into()?); } Self::Obj(builder.build()) } - } + }) } } --- a/crates/jrsonnet-evaluator/src/trace/mod.rs +++ b/crates/jrsonnet-evaluator/src/trace/mod.rs @@ -98,7 +98,7 @@ let mut n = self.resolver.resolve(path); let mut offset = error.location.offset; let is_eof = if offset >= source_code.len() { - offset = source_code.len() - 1; + offset = source_code.len().saturating_sub(1); true } else { false --- a/crates/jrsonnet-stdlib/src/std.jsonnet +++ b/crates/jrsonnet-stdlib/src/std.jsonnet @@ -23,6 +23,7 @@ trace:: $intrinsic(trace), id:: $intrinsic(id), parseJson:: $intrinsic(parseJson), + parseYaml:: $intrinsic(parseYaml), log:: $intrinsic(log), pow:: $intrinsic(pow), -- gitstuff