git.delta.rocks / jrsonnet / refs/commits / 7b8b823d230b

difftreelog

refactor! implement serde support directly

Yaroslav Bolyukin2022-10-25parent: #2e5acc0.patch.diff
in: master
Instead of relying on `serde_json::Value`.

BREAKING CHANGE: There is no longer `impl Typed for serde_json::Value`,
either parse directly into jrsonnet-evaluator, or use `serde_transcode`

9 files changed

added.envrcdiffbeforeafterboth
--- /dev/null
+++ b/.envrc
@@ -0,0 +1 @@
+use flake
modifiedcmds/jrsonnet/Cargo.tomldiffbeforeafterboth
--- a/cmds/jrsonnet/Cargo.toml
+++ b/cmds/jrsonnet/Cargo.toml
@@ -13,9 +13,7 @@
 # Experimental feature, which allows to preserve order of object fields
 exp-preserve-order = [
     "jrsonnet-evaluator/exp-preserve-order",
-    "jrsonnet-evaluator/exp-serde-preserve-order",
     "jrsonnet-cli/exp-preserve-order",
-    "jrsonnet-cli/exp-serde-preserve-order",
 ]
 # Destructuring of locals
 exp-destruct = ["jrsonnet-evaluator/exp-destruct"]
modifiedcrates/jrsonnet-cli/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-cli/Cargo.toml
+++ b/crates/jrsonnet-cli/Cargo.toml
@@ -11,10 +11,6 @@
     "jrsonnet-evaluator/exp-preserve-order",
     "jrsonnet-stdlib/exp-preserve-order",
 ]
-exp-serde-preserve-order = [
-    "jrsonnet-evaluator/exp-serde-preserve-order",
-    "jrsonnet-stdlib/exp-serde-preserve-order",
-]
 legacy-this-file = ["jrsonnet-stdlib/legacy-this-file"]
 
 [dependencies]
modifiedcrates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -18,7 +18,6 @@
 
 # Allows to preserve field order in objects
 exp-preserve-order = []
-exp-serde-preserve-order = ["serde_json/preserve_order"]
 # Implements field destructuring
 exp-destruct = ["jrsonnet-parser/exp-destruct"]
 # Provide Typed for conversions to/from serde_json::Value type
@@ -40,8 +39,6 @@
 thiserror = "1.0"
 
 serde = "1.0"
-# Optional integration
-serde_json = { version = "1.0.82", optional = true }
 
 anyhow = { version = "1.0", optional = true }
 # Friendly errors
modifiedcrates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/integrations/serde.rs
1use jrsonnet_types::ComplexValType;2use serde_json::{Map, Number, Value};34use crate::{5	error::{Error::*, Result},6	throw,7	typed::Typed,8	ObjValueBuilder, State, Val,9};1011impl Typed for Value {12	const TYPE: &'static ComplexValType = &ComplexValType::Any;1314	fn into_untyped(value: Self, s: State) -> Result<Val> {15		Ok(match value {16			Self::Null => Val::Null,17			Self::Bool(v) => Val::Bool(v),18			Self::Number(n) => Val::Num(n.as_f64().ok_or_else(|| {19				RuntimeError(format!("json number can't be represented as jsonnet: {n}").into())20			})?),21			Self::String(s) => Val::Str((&s as &str).into()),22			Self::Array(a) => {23				let mut out: Vec<Val> = Vec::with_capacity(a.len());24				for v in a {25					out.push(Self::into_untyped(v, s.clone())?);26				}27				Val::Arr(out.into())28			}29			Self::Object(o) => {30				let mut builder = ObjValueBuilder::with_capacity(o.len());31				for (k, v) in o {32					builder33						.member((&k as &str).into())34						.value(s.clone(), Self::into_untyped(v, s.clone())?)?;35				}36				Val::Obj(builder.build())37			}38		})39	}4041	fn from_untyped(value: Val, s: State) -> Result<Self> {42		Ok(match value {43			Val::Bool(b) => Self::Bool(b),44			Val::Null => Self::Null,45			Val::Str(s) => Self::String((&s as &str).into()),46			Val::Num(n) => Self::Number(if n.fract() <= f64::EPSILON {47				(n as i64).into()48			} else {49				Number::from_f64(n).expect("jsonnet numbers can't be infinite or NaN")50			}),51			Val::Arr(a) => {52				let mut out = Vec::with_capacity(a.len());53				for item in a.iter(s.clone()) {54					out.push(Self::from_untyped(item?, s.clone())?);55				}56				Self::Array(out)57			}58			Val::Obj(o) => {59				let mut out = Map::new();60				for key in o.fields(61					#[cfg(feature = "exp-preserve-order")]62					cfg!(feature = "exp-serde-preserve-order"),63				) {64					out.insert(65						(&key as &str).into(),66						Self::from_untyped(67							o.get(s.clone(), key)?68								.expect("key is present in fields, so value should exist"),69							s.clone(),70						)?,71					);72				}73				Self::Object(out)74			}75			Val::Func(_) => throw!("tried to manifest function"),76		})77	}78}
after · crates/jrsonnet-evaluator/src/integrations/serde.rs
1use std::borrow::Cow;23use jrsonnet_gcmodule::Cc;4use serde::{de::Visitor, ser::Error, Deserialize, Serialize};56use crate::{error::Result, val::ArrValue, ObjValueBuilder, State, Val};78impl<'de> Deserialize<'de> for Val {9	fn deserialize<D>(deserializer: D) -> Result<Val, D::Error>10	where11		D: serde::Deserializer<'de>,12	{13		struct ValVisitor;1415		// macro_rules! visit_num {16		// 	($($method:ident => $ty:ty),* $(,)?) => {$(17		// 		fn $method<E>(self, v: $ty) -> Result<Self::Value, E>18		// 		where19		// 			E: serde::de::Error,20		// 		{21		// 			Ok(Val::Num(f64::from(v)))22		// 		}23		// 	)*};24		// }2526		impl<'de> Visitor<'de> for ValVisitor {27			type Value = Val;2829			fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>30			where31				E: serde::de::Error,32			{33				Ok(Val::Bool(v))34			}35			fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>36			where37				E: serde::de::Error,38			{39				if !v.is_finite() {40					return Err(E::custom("only finite numbers are supported"));41				}42				Ok(Val::Num(v))43			}44			fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>45			where46				E: serde::de::Error,47			{48				Ok(Val::Str(v.into()))49			}5051			// visit_num! {52			// 	visit_i8 => i8,53			// 	visit_i16 => i16,54			// 	visit_i32 => i32,55			// 	visit_u8 => u8,56			// 	visit_u16 => u16,57			// 	visit_u32 => u32,58			// }59			fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>60			where61				E: serde::de::Error,62			{63				Ok(Val::Num(v as f64))64			}65			fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>66			where67				E: serde::de::Error,68			{69				Ok(Val::Num(v as f64))70			}7172			fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>73			where74				E: serde::de::Error,75			{76				Ok(Val::Arr(ArrValue::Bytes(v.into())))77			}7879			fn visit_none<E>(self) -> Result<Self::Value, E>80			where81				E: serde::de::Error,82			{83				Ok(Val::Null)84			}85			fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>86			where87				D: serde::Deserializer<'de>,88			{89				deserializer.deserialize_any(self)90			}9192			fn visit_unit<E>(self) -> Result<Self::Value, E>93			where94				E: serde::de::Error,95			{96				Ok(Val::Null)97			}9899			fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>100			where101				D: serde::Deserializer<'de>,102			{103				deserializer.deserialize_any(self)104			}105106			fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>107			where108				A: serde::de::SeqAccess<'de>,109			{110				let mut out = seq.size_hint().map_or_else(Vec::new, Vec::with_capacity);111112				while let Some(val) = seq.next_element::<Val>()? {113					out.push(val);114				}115116				Ok(Val::Arr(ArrValue::Eager(Cc::new(out))))117			}118119			fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>120			where121				A: serde::de::MapAccess<'de>,122			{123				let mut out = map124					.size_hint()125					.map_or_else(ObjValueBuilder::new, ObjValueBuilder::with_capacity);126127				while let Some((k, v)) = map.next_entry::<Cow<'de, str>, Val>()? {128					// Jsonnet ignores duplicate keys129					out.member(k.into()).value_unchecked(v);130				}131132				Ok(Val::Obj(out.build()))133			}134135			fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {136				write!(formatter, "any valid jsonnet value")137			}138		}139		deserializer.deserialize_any(ValVisitor)140	}141}142143impl Serialize for Val {144	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>145	where146		S: serde::Serializer,147	{148		match self {149			Val::Bool(v) => serializer.serialize_bool(*v),150			Val::Null => serializer.serialize_none(),151			Val::Str(s) => serializer.serialize_str(s),152			Val::Num(n) => serializer.serialize_f64(*n),153			Val::Arr(arr) => {154				let mut seq = serializer.serialize_seq(Some(arr.len()))?;155				for element in arr.iter(State::default()) {156					// seq.serialize_element()157				}158				todo!()159			}160			Val::Obj(_) => todo!(),161			Val::Func(_) => Err(S::Error::custom("tried to manifest function")),162		}163	}164}
modifiedcrates/jrsonnet-stdlib/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/Cargo.toml
+++ b/crates/jrsonnet-stdlib/Cargo.toml
@@ -15,18 +15,9 @@
 legacy-this-file = []
 # Add order preservation flag to some functions
 exp-preserve-order = ["jrsonnet-evaluator/exp-preserve-order"]
-# Preserve order for files parsed via `std.parseJson`
-# Shame it isn't possible to enable per parse call, instead of globally
-exp-serde-preserve-order = [
-    "serde_json/preserve_order",
-    "jrsonnet-evaluator/exp-serde-preserve-order",
-]
 
 [dependencies]
-jrsonnet-evaluator = { path = "../jrsonnet-evaluator", features = [
-    # std.parseJson parses file via serde, then converts Value to evaluator Val
-    "serde_json",
-], version = "0.4.2" }
+jrsonnet-evaluator = { path = "../jrsonnet-evaluator", version = "0.4.2" }
 jrsonnet-macros = { path = "../jrsonnet-macros", version = "0.4.2" }
 jrsonnet-parser = { path = "../jrsonnet-parser", version = "0.4.2" }
 jrsonnet-gcmodule = "0.3.4"
modifiedcrates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/lib.rs
+++ b/crates/jrsonnet-stdlib/src/lib.rs
@@ -6,16 +6,13 @@
 
 use jrsonnet_evaluator::{
 	error::{Error::*, Result},
-	function::{builtin::Builtin, ArgLike, CallLocation, FuncVal, TlaArg},
+	function::{builtin::Builtin, CallLocation, FuncVal, TlaArg},
 	gc::{GcHashMap, TraceBox},
-	tb, throw,
+	tb,
 	trace::PathResolver,
-	typed::{Any, Either, Either2, Either4, VecVal, M1},
-	val::{equals, ArrValue},
 	Context, ContextBuilder, IStr, ObjValue, ObjValueBuilder, State, Thunk, Val,
 };
 use jrsonnet_gcmodule::Cc;
-use jrsonnet_macros::builtin;
 use jrsonnet_parser::Source;
 
 mod expr;
modifiedcrates/jrsonnet-stdlib/src/parse.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/parse.rs
+++ b/crates/jrsonnet-stdlib/src/parse.rs
@@ -1,22 +1,20 @@
 use jrsonnet_evaluator::{
 	error::{Error::RuntimeError, Result},
 	function::builtin,
-	typed::{Any, Typed},
-	IStr, State, Val,
+	typed::Any,
+	IStr, Val,
 };
 use serde::Deserialize;
 
 #[builtin]
-pub fn builtin_parse_json(st: State, s: IStr) -> Result<Any> {
-	use serde_json::Value;
-	let value: Value = serde_json::from_str(&s)
+pub fn builtin_parse_json(s: IStr) -> Result<Any> {
+	let value: Val = serde_json::from_str(&s)
 		.map_err(|e| RuntimeError(format!("failed to parse json: {}", e).into()))?;
-	Ok(Any(Value::into_untyped(value, st)?))
+	Ok(Any(value))
 }
 
 #[builtin]
-pub fn builtin_parse_yaml(st: State, s: IStr) -> Result<Any> {
-	use serde_json::Value;
+pub fn builtin_parse_yaml(s: IStr) -> Result<Any> {
 	use serde_yaml_with_quirks::DeserializingQuirks;
 	let value = serde_yaml_with_quirks::Deserializer::from_str_with_quirks(
 		&s,
@@ -24,9 +22,8 @@
 	);
 	let mut out = vec![];
 	for item in value {
-		let value = Value::deserialize(item)
+		let val = Val::deserialize(item)
 			.map_err(|e| RuntimeError(format!("failed to parse yaml: {}", e).into()))?;
-		let val = Value::into_untyped(value, st.clone())?;
 		out.push(val);
 	}
 	Ok(Any(if out.is_empty() {
addedflake.nixdiffbeforeafterboth
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,28 @@
+{
+  description = "Jrsonnet";
+  inputs = {
+    nixpkgs.url = "github:nixos/nixpkgs";
+    flake-utils.url = "github:numtide/flake-utils";
+    rust-overlay.url = "github:oxalica/rust-overlay";
+  };
+  outputs = { nixpkgs, flake-utils, rust-overlay, ... }:
+    flake-utils.lib.eachDefaultSystem (system:
+      let
+        pkgs = import nixpkgs {
+          inherit system;
+          overlays = [ rust-overlay.overlays.default ];
+        };
+        rust = ((pkgs.rustChannelOf { date = "2022-10-22"; channel = "nightly"; }).default.override {
+          extensions = [ "rust-src" ];
+        });
+      in
+      rec {
+        devShell = pkgs.mkShell {
+          nativeBuildInputs = with pkgs;[
+            rust
+            cargo-edit
+          ];
+        };
+      }
+    );
+}